brcm2708: remove 3.14 support
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com> SVN-Revision: 44402
This commit is contained in:
parent
0f4431758b
commit
146c9aff27
55 changed files with 0 additions and 286113 deletions
|
@ -1,249 +0,0 @@
|
|||
# CONFIG_AIO is not set
|
||||
CONFIG_ALIGNMENT_TRAP=y
|
||||
# CONFIG_APM_EMULATION is not set
|
||||
CONFIG_ARCH_BCM2708=y
|
||||
CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y
|
||||
CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y
|
||||
CONFIG_ARCH_NR_GPIO=0
|
||||
CONFIG_ARCH_REQUIRE_GPIOLIB=y
|
||||
# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
|
||||
# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
|
||||
CONFIG_ARCH_SUSPEND_POSSIBLE=y
|
||||
# CONFIG_ARCH_USES_GETTIMEOFFSET is not set
|
||||
CONFIG_ARM=y
|
||||
CONFIG_ARM_AMBA=y
|
||||
CONFIG_ARM_CPU_SUSPEND=y
|
||||
CONFIG_ARM_ERRATA_411920=y
|
||||
CONFIG_ARM_L1_CACHE_SHIFT=5
|
||||
CONFIG_ARM_NR_BANKS=8
|
||||
# CONFIG_ARM_SP805_WATCHDOG is not set
|
||||
CONFIG_ARM_THUMB=y
|
||||
CONFIG_ARM_UNWIND=y
|
||||
CONFIG_AVERAGE=y
|
||||
# CONFIG_BACKLIGHT_ADP8860 is not set
|
||||
# CONFIG_BACKLIGHT_ADP8870 is not set
|
||||
CONFIG_BACKLIGHT_LCD_SUPPORT=y
|
||||
# CONFIG_BACKLIGHT_CLASS_DEVICE is not set
|
||||
CONFIG_BCM2708_DMAER=y
|
||||
CONFIG_BCM2708_GPIO=y
|
||||
# CONFIG_BCM2708_NOL2CACHE is not set
|
||||
CONFIG_BCM2708_VCHIQ=y
|
||||
CONFIG_BCM2708_VCMEM=y
|
||||
CONFIG_BCM2708_WDT=y
|
||||
# CONFIG_BLK_DEV_INITRD is not set
|
||||
CONFIG_BLK_DEV_LOOP=y
|
||||
CONFIG_BLK_DEV_RAM=y
|
||||
CONFIG_BLK_DEV_RAM_COUNT=16
|
||||
CONFIG_BLK_DEV_RAM_SIZE=4096
|
||||
CONFIG_BLK_DEV_SD=y
|
||||
CONFIG_BRCM_CHAR_DRIVERS=y
|
||||
# CONFIG_CACHE_L2X0 is not set
|
||||
CONFIG_CLKDEV_LOOKUP=y
|
||||
CONFIG_CMDLINE="dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext3 rootwait"
|
||||
CONFIG_CMDLINE_FROM_BOOTLOADER=y
|
||||
CONFIG_CONFIGFS_FS=y
|
||||
CONFIG_CONSOLE_TRANSLATIONS=y
|
||||
CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y
|
||||
CONFIG_CPU_32v6=y
|
||||
CONFIG_CPU_ABRT_EV6=y
|
||||
# CONFIG_CPU_BPREDICT_DISABLE is not set
|
||||
CONFIG_CPU_CACHE_V6=y
|
||||
CONFIG_CPU_CACHE_VIPT=y
|
||||
CONFIG_CPU_COPY_V6=y
|
||||
CONFIG_CPU_CP15=y
|
||||
CONFIG_CPU_CP15_MMU=y
|
||||
CONFIG_CPU_HAS_ASID=y
|
||||
CONFIG_CPU_HAS_PMU=y
|
||||
# CONFIG_CPU_ICACHE_DISABLE is not set
|
||||
CONFIG_CPU_IDLE=y
|
||||
CONFIG_CPU_IDLE_GOV_LADDER=y
|
||||
CONFIG_CPU_IDLE_GOV_MENU=y
|
||||
CONFIG_CPU_PABRT_V6=y
|
||||
CONFIG_CPU_PM=y
|
||||
CONFIG_CPU_TLB_V6=y
|
||||
CONFIG_CPU_USE_DOMAINS=y
|
||||
CONFIG_CPU_V6=y
|
||||
CONFIG_CRC16=y
|
||||
CONFIG_DEBUG_BUGVERBOSE=y
|
||||
CONFIG_DEBUG_INFO=y
|
||||
# CONFIG_DEBUG_USER is not set
|
||||
CONFIG_DEFAULT_CFQ=y
|
||||
# CONFIG_DEFAULT_DEADLINE is not set
|
||||
CONFIG_DEFAULT_IOSCHED="cfq"
|
||||
CONFIG_DEVTMPFS=y
|
||||
CONFIG_DNOTIFY=y
|
||||
CONFIG_DUMMY_CONSOLE=y
|
||||
CONFIG_ELF_CORE=y
|
||||
CONFIG_ENABLE_MUST_CHECK=y
|
||||
CONFIG_EXT4_FS=y
|
||||
CONFIG_EXT4_FS_POSIX_ACL=y
|
||||
CONFIG_EXT4_FS_SECURITY=y
|
||||
CONFIG_FB=y
|
||||
CONFIG_FB_BCM2708=y
|
||||
CONFIG_FB_CFB_COPYAREA=y
|
||||
CONFIG_FB_CFB_FILLRECT=y
|
||||
CONFIG_FB_CFB_IMAGEBLIT=y
|
||||
# CONFIG_FB_WMT_GE_ROPS is not set
|
||||
CONFIG_FIRMWARE_IN_KERNEL=y
|
||||
# CONFIG_FONTS is not set
|
||||
CONFIG_FONT_8x16=y
|
||||
CONFIG_FONT_8x8=y
|
||||
# CONFIG_FPE_FASTFPE is not set
|
||||
# CONFIG_FPE_NWFPE is not set
|
||||
CONFIG_FRAMEBUFFER_CONSOLE=y
|
||||
# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
|
||||
# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
|
||||
CONFIG_FREEZER=y
|
||||
CONFIG_FS_MBCACHE=y
|
||||
CONFIG_FS_POSIX_ACL=y
|
||||
CONFIG_GENERIC_ACL=y
|
||||
CONFIG_GENERIC_ATOMIC64=y
|
||||
CONFIG_GENERIC_BUG=y
|
||||
CONFIG_GENERIC_CLOCKEVENTS=y
|
||||
CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
|
||||
CONFIG_GENERIC_GPIO=y
|
||||
CONFIG_GENERIC_IRQ_SHOW=y
|
||||
CONFIG_GENERIC_PCI_IOMAP=y
|
||||
CONFIG_GPIOLIB=y
|
||||
CONFIG_GPIO_SYSFS=y
|
||||
# CONFIG_HAMRADIO is not set
|
||||
CONFIG_HARDIRQS_SW_RESEND=y
|
||||
CONFIG_HAS_DMA=y
|
||||
CONFIG_HAS_IOMEM=y
|
||||
CONFIG_HAS_IOPORT=y
|
||||
CONFIG_HAVE_AOUT=y
|
||||
CONFIG_HAVE_ARCH_KGDB=y
|
||||
CONFIG_HAVE_ARCH_PFN_VALID=y
|
||||
CONFIG_HAVE_CLK=y
|
||||
CONFIG_HAVE_C_RECORDMCOUNT=y
|
||||
CONFIG_HAVE_DMA_API_DEBUG=y
|
||||
CONFIG_HAVE_DYNAMIC_FTRACE=y
|
||||
CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
|
||||
CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
|
||||
CONFIG_HAVE_FUNCTION_TRACER=y
|
||||
CONFIG_HAVE_GENERIC_DMA_COHERENT=y
|
||||
CONFIG_HAVE_GENERIC_HARDIRQS=y
|
||||
CONFIG_HAVE_IRQ_WORK=y
|
||||
CONFIG_HAVE_KERNEL_GZIP=y
|
||||
CONFIG_HAVE_KERNEL_LZMA=y
|
||||
CONFIG_HAVE_KERNEL_LZO=y
|
||||
CONFIG_HAVE_KERNEL_XZ=y
|
||||
CONFIG_HAVE_LATENCYTOP_SUPPORT=y
|
||||
CONFIG_HAVE_MEMBLOCK=y
|
||||
CONFIG_HAVE_OPROFILE=y
|
||||
CONFIG_HAVE_PERF_EVENTS=y
|
||||
CONFIG_HAVE_PROC_CPU=y
|
||||
CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
|
||||
CONFIG_HAVE_SPARSE_IRQ=y
|
||||
CONFIG_HW_CONSOLE=y
|
||||
CONFIG_IKCONFIG=y
|
||||
CONFIG_IKCONFIG_PROC=y
|
||||
CONFIG_INPUT=y
|
||||
CONFIG_INPUT_MOUSEDEV=y
|
||||
# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
|
||||
CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
|
||||
CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
|
||||
CONFIG_IOSCHED_CFQ=y
|
||||
CONFIG_JBD2=y
|
||||
CONFIG_KERNEL_GZIP=y
|
||||
# CONFIG_KERNEL_XZ is not set
|
||||
CONFIG_KTIME_SCALAR=y
|
||||
# CONFIG_LCD_AMS369FG06 is not set
|
||||
# CONFIG_LCD_CLASS_DEVICE is not set
|
||||
# CONFIG_LCD_L4F00242T03 is not set
|
||||
# CONFIG_LCD_LD9040 is not set
|
||||
# CONFIG_LCD_LMS283GF05 is not set
|
||||
# CONFIG_LCD_LTV350QV is not set
|
||||
# CONFIG_LCD_PLATFORM is not set
|
||||
# CONFIG_LCD_S6E63M0 is not set
|
||||
# CONFIG_LCD_TDO24M is not set
|
||||
# CONFIG_LCD_VGG2432A4 is not set
|
||||
CONFIG_LEDS_GPIO=y
|
||||
# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set
|
||||
# CONFIG_LEDS_TRIGGER_TIMER is not set
|
||||
CONFIG_LOGO=y
|
||||
CONFIG_LOGO_LINUX_CLUT224=y
|
||||
# CONFIG_LOGO_LINUX_MONO is not set
|
||||
# CONFIG_LOGO_LINUX_VGA16 is not set
|
||||
CONFIG_MACH_BCM2708=y
|
||||
CONFIG_MAC_PARTITION=y
|
||||
CONFIG_MAGIC_SYSRQ=y
|
||||
CONFIG_MAX_RAW_DEVS=256
|
||||
CONFIG_MMC=y
|
||||
# CONFIG_MMC_BCM2708 is not set
|
||||
CONFIG_MMC_BLOCK=y
|
||||
CONFIG_MMC_SDHCI=y
|
||||
CONFIG_MMC_SDHCI_BCM2708=y
|
||||
CONFIG_MMC_SDHCI_BCM2708_DMA=y
|
||||
CONFIG_MMC_SDHCI_IO_ACCESSORS=y
|
||||
CONFIG_MMC_SDHCI_PLTFM=y
|
||||
# CONFIG_MTD is not set
|
||||
CONFIG_NEED_DMA_MAP_STATE=y
|
||||
CONFIG_NEED_MACH_MEMORY_H=y
|
||||
CONFIG_NEED_PER_CPU_KM=y
|
||||
CONFIG_NLS=y
|
||||
CONFIG_NLS_ASCII=y
|
||||
CONFIG_NLS_DEFAULT="utf8"
|
||||
CONFIG_NO_HZ=y
|
||||
CONFIG_OABI_COMPAT=y
|
||||
CONFIG_PAGEFLAGS_EXTENDED=y
|
||||
CONFIG_PAGE_OFFSET=0xC0000000
|
||||
# CONFIG_PCI_SYSCALL is not set
|
||||
# CONFIG_PDA_POWER is not set
|
||||
CONFIG_PERF_USE_VMALLOC=y
|
||||
CONFIG_PM=y
|
||||
CONFIG_PM_CLK=y
|
||||
# CONFIG_PM_DEBUG is not set
|
||||
CONFIG_PM_SLEEP=y
|
||||
CONFIG_POWER_SUPPLY=y
|
||||
# CONFIG_PREEMPT_RCU is not set
|
||||
CONFIG_PRINTK_TIME=y
|
||||
CONFIG_PROC_PAGE_MONITOR=y
|
||||
CONFIG_RAW_DRIVER=y
|
||||
# CONFIG_RTL8192CU is not set
|
||||
CONFIG_SCSI=y
|
||||
# CONFIG_SCSI_LOWLEVEL is not set
|
||||
# CONFIG_SCSI_PROC_FS is not set
|
||||
# CONFIG_SENSORS_BCM2835 is not set
|
||||
# CONFIG_SERIAL_8250 is not set
|
||||
# CONFIG_SERIAL_AMBA_PL010 is not set
|
||||
CONFIG_SERIAL_AMBA_PL011=y
|
||||
CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
|
||||
# CONFIG_SND_BCM2708_SOC_I2S is not set
|
||||
# CONFIG_SQUASHFS is not set
|
||||
# CONFIG_STAGING is not set
|
||||
# CONFIG_STRIP_ASM_SYMS is not set
|
||||
CONFIG_SUSPEND=y
|
||||
CONFIG_SUSPEND_FREEZER=y
|
||||
CONFIG_SYS_SUPPORTS_APM_EMULATION=y
|
||||
# CONFIG_TEXTSEARCH is not set
|
||||
CONFIG_TMPFS_POSIX_ACL=y
|
||||
CONFIG_UEVENT_HELPER_PATH=""
|
||||
# CONFIG_UID16 is not set
|
||||
CONFIG_USB=y
|
||||
CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
|
||||
# CONFIG_USB_ARCH_HAS_EHCI is not set
|
||||
# CONFIG_USB_ARCH_HAS_OHCI is not set
|
||||
# CONFIG_USB_ARCH_HAS_XHCI is not set
|
||||
CONFIG_USB_COMMON=y
|
||||
# CONFIG_USB_DEVICEFS is not set
|
||||
CONFIG_USB_DEVICE_CLASS=y
|
||||
CONFIG_USB_DWCOTG=y
|
||||
CONFIG_USB_LIBUSUAL=y
|
||||
CONFIG_USB_NET_SMSC95XX=y
|
||||
CONFIG_USB_STORAGE=y
|
||||
CONFIG_USB_SUPPORT=y
|
||||
CONFIG_USB_UAS=y
|
||||
CONFIG_USB_USBNET=y
|
||||
CONFIG_VECTORS_BASE=0xffff0000
|
||||
CONFIG_VFP=y
|
||||
# CONFIG_VIDEO_BCM2835 is not set
|
||||
CONFIG_VT=y
|
||||
CONFIG_VT_CONSOLE=y
|
||||
CONFIG_VT_CONSOLE_SLEEP=y
|
||||
CONFIG_VT_HW_CONSOLE_BINDING=y
|
||||
CONFIG_XZ_DEC_ARM=y
|
||||
CONFIG_XZ_DEC_BCJ=y
|
||||
CONFIG_ZBOOT_ROM_BSS=0x0
|
||||
CONFIG_ZBOOT_ROM_TEXT=0x0
|
||||
CONFIG_ZONE_DMA_FLAG=0
|
File diff suppressed because it is too large
Load diff
|
@ -1,212 +0,0 @@
|
|||
From 99871c2479dd1d1eaab88f738a77ca4846906b35 Mon Sep 17 00:00:00 2001
|
||||
From: popcornmix <popcornmix@gmail.com>
|
||||
Date: Tue, 7 May 2013 22:20:24 +0100
|
||||
Subject: [PATCH 02/54] Add quick config.
|
||||
|
||||
This is designed for quick compiling when developing.
|
||||
No modules are needed and it includes all Pi specific drivers
|
||||
---
|
||||
arch/arm/configs/bcmrpi_quick_defconfig | 197 ++++++++++++++++++++++++++++++++
|
||||
1 file changed, 197 insertions(+)
|
||||
create mode 100644 arch/arm/configs/bcmrpi_quick_defconfig
|
||||
|
||||
--- /dev/null
|
||||
+++ b/arch/arm/configs/bcmrpi_quick_defconfig
|
||||
@@ -0,0 +1,197 @@
|
||||
+# CONFIG_ARM_PATCH_PHYS_VIRT is not set
|
||||
+CONFIG_LOCALVERSION="-quick"
|
||||
+# CONFIG_LOCALVERSION_AUTO is not set
|
||||
+# CONFIG_SWAP is not set
|
||||
+CONFIG_SYSVIPC=y
|
||||
+CONFIG_POSIX_MQUEUE=y
|
||||
+CONFIG_NO_HZ=y
|
||||
+CONFIG_HIGH_RES_TIMERS=y
|
||||
+CONFIG_IKCONFIG=y
|
||||
+CONFIG_IKCONFIG_PROC=y
|
||||
+CONFIG_KALLSYMS_ALL=y
|
||||
+CONFIG_EMBEDDED=y
|
||||
+CONFIG_PERF_EVENTS=y
|
||||
+# CONFIG_COMPAT_BRK is not set
|
||||
+CONFIG_SLAB=y
|
||||
+CONFIG_MODULES=y
|
||||
+CONFIG_MODULE_UNLOAD=y
|
||||
+CONFIG_MODVERSIONS=y
|
||||
+CONFIG_MODULE_SRCVERSION_ALL=y
|
||||
+# CONFIG_BLK_DEV_BSG is not set
|
||||
+CONFIG_ARCH_BCM2708=y
|
||||
+CONFIG_PREEMPT=y
|
||||
+CONFIG_AEABI=y
|
||||
+CONFIG_UACCESS_WITH_MEMCPY=y
|
||||
+CONFIG_ZBOOT_ROM_TEXT=0x0
|
||||
+CONFIG_ZBOOT_ROM_BSS=0x0
|
||||
+CONFIG_CMDLINE="dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait"
|
||||
+CONFIG_CPU_FREQ=y
|
||||
+CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE=y
|
||||
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
|
||||
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
|
||||
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
|
||||
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
|
||||
+CONFIG_CPU_IDLE=y
|
||||
+CONFIG_VFP=y
|
||||
+CONFIG_BINFMT_MISC=y
|
||||
+CONFIG_NET=y
|
||||
+CONFIG_PACKET=y
|
||||
+CONFIG_UNIX=y
|
||||
+CONFIG_INET=y
|
||||
+CONFIG_IP_MULTICAST=y
|
||||
+CONFIG_IP_PNP=y
|
||||
+CONFIG_IP_PNP_DHCP=y
|
||||
+CONFIG_IP_PNP_RARP=y
|
||||
+CONFIG_SYN_COOKIES=y
|
||||
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
|
||||
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
|
||||
+# CONFIG_INET_XFRM_MODE_BEET is not set
|
||||
+# CONFIG_INET_LRO is not set
|
||||
+# CONFIG_INET_DIAG is not set
|
||||
+# CONFIG_IPV6 is not set
|
||||
+# CONFIG_WIRELESS is not set
|
||||
+CONFIG_DEVTMPFS=y
|
||||
+CONFIG_DEVTMPFS_MOUNT=y
|
||||
+CONFIG_BLK_DEV_LOOP=y
|
||||
+CONFIG_BLK_DEV_RAM=y
|
||||
+CONFIG_SCSI=y
|
||||
+# CONFIG_SCSI_PROC_FS is not set
|
||||
+# CONFIG_SCSI_LOWLEVEL is not set
|
||||
+CONFIG_NETDEVICES=y
|
||||
+# CONFIG_NET_VENDOR_BROADCOM is not set
|
||||
+# CONFIG_NET_VENDOR_CIRRUS is not set
|
||||
+# CONFIG_NET_VENDOR_FARADAY is not set
|
||||
+# CONFIG_NET_VENDOR_INTEL is not set
|
||||
+# CONFIG_NET_VENDOR_MARVELL is not set
|
||||
+# CONFIG_NET_VENDOR_MICREL is not set
|
||||
+# CONFIG_NET_VENDOR_NATSEMI is not set
|
||||
+# CONFIG_NET_VENDOR_SEEQ is not set
|
||||
+# CONFIG_NET_VENDOR_STMICRO is not set
|
||||
+# CONFIG_NET_VENDOR_WIZNET is not set
|
||||
+CONFIG_USB_USBNET=y
|
||||
+# CONFIG_USB_NET_AX8817X is not set
|
||||
+# CONFIG_USB_NET_CDCETHER is not set
|
||||
+# CONFIG_USB_NET_CDC_NCM is not set
|
||||
+CONFIG_USB_NET_SMSC95XX=y
|
||||
+# CONFIG_USB_NET_NET1080 is not set
|
||||
+# CONFIG_USB_NET_CDC_SUBSET is not set
|
||||
+# CONFIG_USB_NET_ZAURUS is not set
|
||||
+# CONFIG_WLAN is not set
|
||||
+# CONFIG_INPUT_MOUSEDEV is not set
|
||||
+CONFIG_INPUT_EVDEV=y
|
||||
+# CONFIG_INPUT_KEYBOARD is not set
|
||||
+# CONFIG_INPUT_MOUSE is not set
|
||||
+# CONFIG_SERIO is not set
|
||||
+CONFIG_VT_HW_CONSOLE_BINDING=y
|
||||
+# CONFIG_LEGACY_PTYS is not set
|
||||
+# CONFIG_DEVKMEM is not set
|
||||
+CONFIG_SERIAL_AMBA_PL011=y
|
||||
+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
|
||||
+CONFIG_TTY_PRINTK=y
|
||||
+CONFIG_HW_RANDOM=y
|
||||
+CONFIG_HW_RANDOM_BCM2708=y
|
||||
+CONFIG_RAW_DRIVER=y
|
||||
+CONFIG_THERMAL=y
|
||||
+CONFIG_THERMAL_BCM2835=y
|
||||
+CONFIG_WATCHDOG=y
|
||||
+CONFIG_BCM2708_WDT=y
|
||||
+CONFIG_REGULATOR=y
|
||||
+CONFIG_REGULATOR_DEBUG=y
|
||||
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
|
||||
+CONFIG_REGULATOR_VIRTUAL_CONSUMER=y
|
||||
+CONFIG_REGULATOR_USERSPACE_CONSUMER=y
|
||||
+CONFIG_FB=y
|
||||
+CONFIG_FB_BCM2708=y
|
||||
+CONFIG_FRAMEBUFFER_CONSOLE=y
|
||||
+CONFIG_LOGO=y
|
||||
+# CONFIG_LOGO_LINUX_MONO is not set
|
||||
+# CONFIG_LOGO_LINUX_VGA16 is not set
|
||||
+CONFIG_SOUND=y
|
||||
+CONFIG_SND=y
|
||||
+CONFIG_SND_BCM2835=y
|
||||
+# CONFIG_SND_USB is not set
|
||||
+CONFIG_USB=y
|
||||
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
|
||||
+CONFIG_USB_DWCOTG=y
|
||||
+CONFIG_MMC=y
|
||||
+CONFIG_MMC_SDHCI=y
|
||||
+CONFIG_MMC_SDHCI_PLTFM=y
|
||||
+CONFIG_MMC_SDHCI_BCM2708=y
|
||||
+CONFIG_MMC_SDHCI_BCM2708_DMA=y
|
||||
+CONFIG_NEW_LEDS=y
|
||||
+CONFIG_LEDS_CLASS=y
|
||||
+CONFIG_LEDS_TRIGGERS=y
|
||||
+# CONFIG_IOMMU_SUPPORT is not set
|
||||
+CONFIG_EXT4_FS=y
|
||||
+CONFIG_EXT4_FS_POSIX_ACL=y
|
||||
+CONFIG_EXT4_FS_SECURITY=y
|
||||
+CONFIG_AUTOFS4_FS=y
|
||||
+CONFIG_FSCACHE=y
|
||||
+CONFIG_CACHEFILES=y
|
||||
+CONFIG_MSDOS_FS=y
|
||||
+CONFIG_VFAT_FS=y
|
||||
+CONFIG_FAT_DEFAULT_IOCHARSET="ascii"
|
||||
+CONFIG_TMPFS=y
|
||||
+CONFIG_TMPFS_POSIX_ACL=y
|
||||
+CONFIG_CONFIGFS_FS=y
|
||||
+# CONFIG_MISC_FILESYSTEMS is not set
|
||||
+CONFIG_NFS_FS=y
|
||||
+CONFIG_NFS_V3_ACL=y
|
||||
+CONFIG_NFS_V4=y
|
||||
+CONFIG_ROOT_NFS=y
|
||||
+CONFIG_NFS_FSCACHE=y
|
||||
+CONFIG_NLS_DEFAULT="utf8"
|
||||
+CONFIG_NLS_CODEPAGE_437=y
|
||||
+CONFIG_NLS_CODEPAGE_737=y
|
||||
+CONFIG_NLS_CODEPAGE_775=y
|
||||
+CONFIG_NLS_CODEPAGE_850=y
|
||||
+CONFIG_NLS_CODEPAGE_852=y
|
||||
+CONFIG_NLS_CODEPAGE_855=y
|
||||
+CONFIG_NLS_CODEPAGE_857=y
|
||||
+CONFIG_NLS_CODEPAGE_860=y
|
||||
+CONFIG_NLS_CODEPAGE_861=y
|
||||
+CONFIG_NLS_CODEPAGE_862=y
|
||||
+CONFIG_NLS_CODEPAGE_863=y
|
||||
+CONFIG_NLS_CODEPAGE_864=y
|
||||
+CONFIG_NLS_CODEPAGE_865=y
|
||||
+CONFIG_NLS_CODEPAGE_866=y
|
||||
+CONFIG_NLS_CODEPAGE_869=y
|
||||
+CONFIG_NLS_CODEPAGE_936=y
|
||||
+CONFIG_NLS_CODEPAGE_950=y
|
||||
+CONFIG_NLS_CODEPAGE_932=y
|
||||
+CONFIG_NLS_CODEPAGE_949=y
|
||||
+CONFIG_NLS_CODEPAGE_874=y
|
||||
+CONFIG_NLS_ISO8859_8=y
|
||||
+CONFIG_NLS_CODEPAGE_1250=y
|
||||
+CONFIG_NLS_CODEPAGE_1251=y
|
||||
+CONFIG_NLS_ASCII=y
|
||||
+CONFIG_NLS_ISO8859_1=y
|
||||
+CONFIG_NLS_ISO8859_2=y
|
||||
+CONFIG_NLS_ISO8859_3=y
|
||||
+CONFIG_NLS_ISO8859_4=y
|
||||
+CONFIG_NLS_ISO8859_5=y
|
||||
+CONFIG_NLS_ISO8859_6=y
|
||||
+CONFIG_NLS_ISO8859_7=y
|
||||
+CONFIG_NLS_ISO8859_9=y
|
||||
+CONFIG_NLS_ISO8859_13=y
|
||||
+CONFIG_NLS_ISO8859_14=y
|
||||
+CONFIG_NLS_ISO8859_15=y
|
||||
+CONFIG_NLS_UTF8=y
|
||||
+CONFIG_PRINTK_TIME=y
|
||||
+CONFIG_DEBUG_FS=y
|
||||
+CONFIG_DETECT_HUNG_TASK=y
|
||||
+# CONFIG_DEBUG_PREEMPT is not set
|
||||
+# CONFIG_DEBUG_BUGVERBOSE is not set
|
||||
+# CONFIG_FTRACE is not set
|
||||
+CONFIG_KGDB=y
|
||||
+CONFIG_KGDB_KDB=y
|
||||
+# CONFIG_ARM_UNWIND is not set
|
||||
+CONFIG_CRYPTO_CBC=y
|
||||
+CONFIG_CRYPTO_HMAC=y
|
||||
+CONFIG_CRYPTO_MD5=y
|
||||
+CONFIG_CRYPTO_SHA1=y
|
||||
+CONFIG_CRYPTO_DES=y
|
||||
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
|
||||
+# CONFIG_CRYPTO_HW is not set
|
||||
+CONFIG_CRC_ITU_T=y
|
||||
+CONFIG_LIBCRC32C=y
|
File diff suppressed because it is too large
Load diff
|
@ -1,425 +0,0 @@
|
|||
From 8711c9547fe7196dcd3adf59265968bc1fc16aad Mon Sep 17 00:00:00 2001
|
||||
From: popcornmix <popcornmix@gmail.com>
|
||||
Date: Wed, 1 May 2013 19:54:32 +0100
|
||||
Subject: [PATCH 04/54] bcm2708 watchdog driver
|
||||
|
||||
Signed-off-by: popcornmix <popcornmix@gmail.com>
|
||||
---
|
||||
drivers/watchdog/Kconfig | 6 +
|
||||
drivers/watchdog/Makefile | 1 +
|
||||
drivers/watchdog/bcm2708_wdog.c | 384 ++++++++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 391 insertions(+)
|
||||
create mode 100644 drivers/watchdog/bcm2708_wdog.c
|
||||
|
||||
--- a/drivers/watchdog/Kconfig
|
||||
+++ b/drivers/watchdog/Kconfig
|
||||
@@ -402,6 +402,12 @@ config RETU_WATCHDOG
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called retu_wdt.
|
||||
|
||||
+config BCM2708_WDT
|
||||
+ tristate "BCM2708 Watchdog"
|
||||
+ depends on ARCH_BCM2708
|
||||
+ help
|
||||
+ Enables BCM2708 watchdog support.
|
||||
+
|
||||
config MOXART_WDT
|
||||
tristate "MOXART watchdog"
|
||||
depends on ARCH_MOXART
|
||||
--- a/drivers/watchdog/Makefile
|
||||
+++ b/drivers/watchdog/Makefile
|
||||
@@ -54,6 +54,7 @@ obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_
|
||||
obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o
|
||||
obj-$(CONFIG_UX500_WATCHDOG) += ux500_wdt.o
|
||||
obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
|
||||
+obj-$(CONFIG_BCM2708_WDT) += bcm2708_wdog.o
|
||||
obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
|
||||
obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o
|
||||
obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/watchdog/bcm2708_wdog.c
|
||||
@@ -0,0 +1,384 @@
|
||||
+/*
|
||||
+ * Broadcom BCM2708 watchdog driver.
|
||||
+ *
|
||||
+ * (c) Copyright 2010 Broadcom Europe Ltd
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU General Public License
|
||||
+ * as published by the Free Software Foundation; either version
|
||||
+ * 2 of the License, or (at your option) any later version.
|
||||
+ *
|
||||
+ * BCM2708 watchdog driver. Loosely based on wdt driver.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/interrupt.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/moduleparam.h>
|
||||
+#include <linux/types.h>
|
||||
+#include <linux/miscdevice.h>
|
||||
+#include <linux/watchdog.h>
|
||||
+#include <linux/fs.h>
|
||||
+#include <linux/ioport.h>
|
||||
+#include <linux/notifier.h>
|
||||
+#include <linux/reboot.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/uaccess.h>
|
||||
+#include <mach/platform.h>
|
||||
+
|
||||
+#include <asm/system.h>
|
||||
+
|
||||
+#define SECS_TO_WDOG_TICKS(x) ((x) << 16)
|
||||
+#define WDOG_TICKS_TO_SECS(x) ((x) >> 16)
|
||||
+
|
||||
+static unsigned long wdog_is_open;
|
||||
+static uint32_t wdog_ticks; /* Ticks to load into wdog timer */
|
||||
+static char expect_close;
|
||||
+
|
||||
+/*
|
||||
+ * Module parameters
|
||||
+ */
|
||||
+
|
||||
+#define WD_TIMO 10 /* Default heartbeat = 60 seconds */
|
||||
+static int heartbeat = WD_TIMO; /* Heartbeat in seconds */
|
||||
+
|
||||
+module_param(heartbeat, int, 0);
|
||||
+MODULE_PARM_DESC(heartbeat,
|
||||
+ "Watchdog heartbeat in seconds. (0 < heartbeat < 65536, default="
|
||||
+ __MODULE_STRING(WD_TIMO) ")");
|
||||
+
|
||||
+static int nowayout = WATCHDOG_NOWAYOUT;
|
||||
+module_param(nowayout, int, 0);
|
||||
+MODULE_PARM_DESC(nowayout,
|
||||
+ "Watchdog cannot be stopped once started (default="
|
||||
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
|
||||
+
|
||||
+static DEFINE_SPINLOCK(wdog_lock);
|
||||
+
|
||||
+/**
|
||||
+ * Start the watchdog driver.
|
||||
+ */
|
||||
+
|
||||
+static int wdog_start(unsigned long timeout)
|
||||
+{
|
||||
+ uint32_t cur;
|
||||
+ unsigned long flags;
|
||||
+ spin_lock_irqsave(&wdog_lock, flags);
|
||||
+
|
||||
+ /* enable the watchdog */
|
||||
+ iowrite32(PM_PASSWORD | (timeout & PM_WDOG_TIME_SET),
|
||||
+ __io_address(PM_WDOG));
|
||||
+ cur = ioread32(__io_address(PM_RSTC));
|
||||
+ iowrite32(PM_PASSWORD | (cur & PM_RSTC_WRCFG_CLR) |
|
||||
+ PM_RSTC_WRCFG_FULL_RESET, __io_address(PM_RSTC));
|
||||
+
|
||||
+ spin_unlock_irqrestore(&wdog_lock, flags);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * Stop the watchdog driver.
|
||||
+ */
|
||||
+
|
||||
+static int wdog_stop(void)
|
||||
+{
|
||||
+ iowrite32(PM_PASSWORD | PM_RSTC_RESET, __io_address(PM_RSTC));
|
||||
+ printk(KERN_INFO "watchdog stopped\n");
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * Reload counter one with the watchdog heartbeat. We don't bother
|
||||
+ * reloading the cascade counter.
|
||||
+ */
|
||||
+
|
||||
+static void wdog_ping(void)
|
||||
+{
|
||||
+ wdog_start(wdog_ticks);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * @t: the new heartbeat value that needs to be set.
|
||||
+ *
|
||||
+ * Set a new heartbeat value for the watchdog device. If the heartbeat
|
||||
+ * value is incorrect we keep the old value and return -EINVAL. If
|
||||
+ * successful we return 0.
|
||||
+ */
|
||||
+
|
||||
+static int wdog_set_heartbeat(int t)
|
||||
+{
|
||||
+ if (t < 1 || t > WDOG_TICKS_TO_SECS(PM_WDOG_TIME_SET))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ heartbeat = t;
|
||||
+ wdog_ticks = SECS_TO_WDOG_TICKS(t);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * @file: file handle to the watchdog
|
||||
+ * @buf: buffer to write (unused as data does not matter here
|
||||
+ * @count: count of bytes
|
||||
+ * @ppos: pointer to the position to write. No seeks allowed
|
||||
+ *
|
||||
+ * A write to a watchdog device is defined as a keepalive signal.
|
||||
+ *
|
||||
+ * if 'nowayout' is set then normally a close() is ignored. But
|
||||
+ * if you write 'V' first then the close() will stop the timer.
|
||||
+ */
|
||||
+
|
||||
+static ssize_t wdog_write(struct file *file, const char __user *buf,
|
||||
+ size_t count, loff_t *ppos)
|
||||
+{
|
||||
+ if (count) {
|
||||
+ if (!nowayout) {
|
||||
+ size_t i;
|
||||
+
|
||||
+ /* In case it was set long ago */
|
||||
+ expect_close = 0;
|
||||
+
|
||||
+ for (i = 0; i != count; i++) {
|
||||
+ char c;
|
||||
+ if (get_user(c, buf + i))
|
||||
+ return -EFAULT;
|
||||
+ if (c == 'V')
|
||||
+ expect_close = 42;
|
||||
+ }
|
||||
+ }
|
||||
+ wdog_ping();
|
||||
+ }
|
||||
+ return count;
|
||||
+}
|
||||
+
|
||||
+static int wdog_get_status(void)
|
||||
+{
|
||||
+ unsigned long flags;
|
||||
+ int status = 0;
|
||||
+ spin_lock_irqsave(&wdog_lock, flags);
|
||||
+ /* FIXME: readback reset reason */
|
||||
+ spin_unlock_irqrestore(&wdog_lock, flags);
|
||||
+ return status;
|
||||
+}
|
||||
+
|
||||
+static uint32_t wdog_get_remaining(void)
|
||||
+{
|
||||
+ uint32_t ret = ioread32(__io_address(PM_WDOG));
|
||||
+ return ret & PM_WDOG_TIME_SET;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * @file: file handle to the device
|
||||
+ * @cmd: watchdog command
|
||||
+ * @arg: argument pointer
|
||||
+ *
|
||||
+ * The watchdog API defines a common set of functions for all watchdogs
|
||||
+ * according to their available features. We only actually usefully support
|
||||
+ * querying capabilities and current status.
|
||||
+ */
|
||||
+
|
||||
+static long wdog_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
+{
|
||||
+ void __user *argp = (void __user *)arg;
|
||||
+ int __user *p = argp;
|
||||
+ int new_heartbeat;
|
||||
+ int status;
|
||||
+ int options;
|
||||
+ uint32_t remaining;
|
||||
+
|
||||
+ struct watchdog_info ident = {
|
||||
+ .options = WDIOF_SETTIMEOUT|
|
||||
+ WDIOF_MAGICCLOSE|
|
||||
+ WDIOF_KEEPALIVEPING,
|
||||
+ .firmware_version = 1,
|
||||
+ .identity = "BCM2708",
|
||||
+ };
|
||||
+
|
||||
+ switch (cmd) {
|
||||
+ case WDIOC_GETSUPPORT:
|
||||
+ return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
|
||||
+ case WDIOC_GETSTATUS:
|
||||
+ status = wdog_get_status();
|
||||
+ return put_user(status, p);
|
||||
+ case WDIOC_GETBOOTSTATUS:
|
||||
+ return put_user(0, p);
|
||||
+ case WDIOC_KEEPALIVE:
|
||||
+ wdog_ping();
|
||||
+ return 0;
|
||||
+ case WDIOC_SETTIMEOUT:
|
||||
+ if (get_user(new_heartbeat, p))
|
||||
+ return -EFAULT;
|
||||
+ if (wdog_set_heartbeat(new_heartbeat))
|
||||
+ return -EINVAL;
|
||||
+ wdog_ping();
|
||||
+ /* Fall */
|
||||
+ case WDIOC_GETTIMEOUT:
|
||||
+ return put_user(heartbeat, p);
|
||||
+ case WDIOC_GETTIMELEFT:
|
||||
+ remaining = WDOG_TICKS_TO_SECS(wdog_get_remaining());
|
||||
+ return put_user(remaining, p);
|
||||
+ case WDIOC_SETOPTIONS:
|
||||
+ if (get_user(options, p))
|
||||
+ return -EFAULT;
|
||||
+ if (options & WDIOS_DISABLECARD)
|
||||
+ wdog_stop();
|
||||
+ if (options & WDIOS_ENABLECARD)
|
||||
+ wdog_start(wdog_ticks);
|
||||
+ return 0;
|
||||
+ default:
|
||||
+ return -ENOTTY;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * @inode: inode of device
|
||||
+ * @file: file handle to device
|
||||
+ *
|
||||
+ * The watchdog device has been opened. The watchdog device is single
|
||||
+ * open and on opening we load the counters.
|
||||
+ */
|
||||
+
|
||||
+static int wdog_open(struct inode *inode, struct file *file)
|
||||
+{
|
||||
+ if (test_and_set_bit(0, &wdog_is_open))
|
||||
+ return -EBUSY;
|
||||
+ /*
|
||||
+ * Activate
|
||||
+ */
|
||||
+ wdog_start(wdog_ticks);
|
||||
+ return nonseekable_open(inode, file);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * @inode: inode to board
|
||||
+ * @file: file handle to board
|
||||
+ *
|
||||
+ * The watchdog has a configurable API. There is a religious dispute
|
||||
+ * between people who want their watchdog to be able to shut down and
|
||||
+ * those who want to be sure if the watchdog manager dies the machine
|
||||
+ * reboots. In the former case we disable the counters, in the latter
|
||||
+ * case you have to open it again very soon.
|
||||
+ */
|
||||
+
|
||||
+static int wdog_release(struct inode *inode, struct file *file)
|
||||
+{
|
||||
+ if (expect_close == 42) {
|
||||
+ wdog_stop();
|
||||
+ } else {
|
||||
+ printk(KERN_CRIT
|
||||
+ "wdt: WDT device closed unexpectedly. WDT will not stop!\n");
|
||||
+ wdog_ping();
|
||||
+ }
|
||||
+ clear_bit(0, &wdog_is_open);
|
||||
+ expect_close = 0;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * @this: our notifier block
|
||||
+ * @code: the event being reported
|
||||
+ * @unused: unused
|
||||
+ *
|
||||
+ * Our notifier is called on system shutdowns. Turn the watchdog
|
||||
+ * off so that it does not fire during the next reboot.
|
||||
+ */
|
||||
+
|
||||
+static int wdog_notify_sys(struct notifier_block *this, unsigned long code,
|
||||
+ void *unused)
|
||||
+{
|
||||
+ if (code == SYS_DOWN || code == SYS_HALT)
|
||||
+ wdog_stop();
|
||||
+ return NOTIFY_DONE;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Kernel Interfaces
|
||||
+ */
|
||||
+
|
||||
+
|
||||
+static const struct file_operations wdog_fops = {
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .llseek = no_llseek,
|
||||
+ .write = wdog_write,
|
||||
+ .unlocked_ioctl = wdog_ioctl,
|
||||
+ .open = wdog_open,
|
||||
+ .release = wdog_release,
|
||||
+};
|
||||
+
|
||||
+static struct miscdevice wdog_miscdev = {
|
||||
+ .minor = WATCHDOG_MINOR,
|
||||
+ .name = "watchdog",
|
||||
+ .fops = &wdog_fops,
|
||||
+};
|
||||
+
|
||||
+/*
|
||||
+ * The WDT card needs to learn about soft shutdowns in order to
|
||||
+ * turn the timebomb registers off.
|
||||
+ */
|
||||
+
|
||||
+static struct notifier_block wdog_notifier = {
|
||||
+ .notifier_call = wdog_notify_sys,
|
||||
+};
|
||||
+
|
||||
+/**
|
||||
+ * cleanup_module:
|
||||
+ *
|
||||
+ * Unload the watchdog. You cannot do this with any file handles open.
|
||||
+ * If your watchdog is set to continue ticking on close and you unload
|
||||
+ * it, well it keeps ticking. We won't get the interrupt but the board
|
||||
+ * will not touch PC memory so all is fine. You just have to load a new
|
||||
+ * module in 60 seconds or reboot.
|
||||
+ */
|
||||
+
|
||||
+static void __exit wdog_exit(void)
|
||||
+{
|
||||
+ misc_deregister(&wdog_miscdev);
|
||||
+ unregister_reboot_notifier(&wdog_notifier);
|
||||
+}
|
||||
+
|
||||
+static int __init wdog_init(void)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ /* Check that the heartbeat value is within it's range;
|
||||
+ if not reset to the default */
|
||||
+ if (wdog_set_heartbeat(heartbeat)) {
|
||||
+ wdog_set_heartbeat(WD_TIMO);
|
||||
+ printk(KERN_INFO "bcm2708_wdog: heartbeat value must be "
|
||||
+ "0 < heartbeat < %d, using %d\n",
|
||||
+ WDOG_TICKS_TO_SECS(PM_WDOG_TIME_SET),
|
||||
+ WD_TIMO);
|
||||
+ }
|
||||
+
|
||||
+ ret = register_reboot_notifier(&wdog_notifier);
|
||||
+ if (ret) {
|
||||
+ printk(KERN_ERR
|
||||
+ "wdt: cannot register reboot notifier (err=%d)\n", ret);
|
||||
+ goto out_reboot;
|
||||
+ }
|
||||
+
|
||||
+ ret = misc_register(&wdog_miscdev);
|
||||
+ if (ret) {
|
||||
+ printk(KERN_ERR
|
||||
+ "wdt: cannot register miscdev on minor=%d (err=%d)\n",
|
||||
+ WATCHDOG_MINOR, ret);
|
||||
+ goto out_misc;
|
||||
+ }
|
||||
+
|
||||
+ printk(KERN_INFO "bcm2708 watchdog, heartbeat=%d sec (nowayout=%d)\n",
|
||||
+ heartbeat, nowayout);
|
||||
+ return 0;
|
||||
+
|
||||
+out_misc:
|
||||
+ unregister_reboot_notifier(&wdog_notifier);
|
||||
+out_reboot:
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+module_init(wdog_init);
|
||||
+module_exit(wdog_exit);
|
||||
+
|
||||
+MODULE_AUTHOR("Luke Diamand");
|
||||
+MODULE_DESCRIPTION("Driver for BCM2708 watchdog");
|
||||
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
|
||||
+MODULE_ALIAS_MISCDEV(TEMP_MINOR);
|
||||
+MODULE_LICENSE("GPL");
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,148 +0,0 @@
|
|||
From 376fdba15bcf4b106ba7d62ee350baf21099cfbf Mon Sep 17 00:00:00 2001
|
||||
From: popcornmix <popcornmix@gmail.com>
|
||||
Date: Mon, 12 May 2014 15:12:02 +0100
|
||||
Subject: [PATCH 07/54] vchiq: Avoid high load when blocked and unkillable
|
||||
|
||||
---
|
||||
.../interface/vchiq_arm/vchiq_2835_arm.c | 1 +
|
||||
.../vc04_services/interface/vchiq_arm/vchiq_arm.c | 1 +
|
||||
.../interface/vchiq_arm/vchiq_connected.c | 1 +
|
||||
.../vc04_services/interface/vchiq_arm/vchiq_core.c | 1 +
|
||||
.../interface/vchiq_arm/vchiq_kern_lib.c | 1 +
|
||||
.../interface/vchiq_arm/vchiq_killable.h | 69 ++++++++++++++++++++++
|
||||
.../vc04_services/interface/vchiq_arm/vchiq_util.c | 1 +
|
||||
7 files changed, 75 insertions(+)
|
||||
create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_killable.h
|
||||
|
||||
--- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c
|
||||
+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c
|
||||
@@ -56,6 +56,7 @@
|
||||
#include "vchiq_arm.h"
|
||||
#include "vchiq_2835.h"
|
||||
#include "vchiq_connected.h"
|
||||
+#include "vchiq_killable.h"
|
||||
|
||||
#define MAX_FRAGMENTS (VCHIQ_NUM_CURRENT_BULKS * 2)
|
||||
|
||||
--- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c
|
||||
+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c
|
||||
@@ -49,6 +49,7 @@
|
||||
#include "vchiq_core.h"
|
||||
#include "vchiq_ioctl.h"
|
||||
#include "vchiq_arm.h"
|
||||
+#include "vchiq_killable.h"
|
||||
|
||||
#define DEVICE_NAME "vchiq"
|
||||
|
||||
--- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.c
|
||||
+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.c
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
#include "vchiq_connected.h"
|
||||
#include "vchiq_core.h"
|
||||
+#include "vchiq_killable.h"
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
--- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c
|
||||
+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c
|
||||
@@ -32,6 +32,7 @@
|
||||
*/
|
||||
|
||||
#include "vchiq_core.h"
|
||||
+#include "vchiq_killable.h"
|
||||
|
||||
#define VCHIQ_SLOT_HANDLER_STACK 8192
|
||||
|
||||
--- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c
|
||||
+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c
|
||||
@@ -39,6 +39,7 @@
|
||||
|
||||
#include "vchiq_core.h"
|
||||
#include "vchiq_arm.h"
|
||||
+#include "vchiq_killable.h"
|
||||
|
||||
/* ---- Public Variables ------------------------------------------------- */
|
||||
|
||||
--- /dev/null
|
||||
+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_killable.h
|
||||
@@ -0,0 +1,69 @@
|
||||
+/**
|
||||
+ * Copyright (c) 2010-2012 Broadcom. All rights reserved.
|
||||
+ *
|
||||
+ * Redistribution and use in source and binary forms, with or without
|
||||
+ * modification, are permitted provided that the following conditions
|
||||
+ * are met:
|
||||
+ * 1. Redistributions of source code must retain the above copyright
|
||||
+ * notice, this list of conditions, and the following disclaimer,
|
||||
+ * without modification.
|
||||
+ * 2. Redistributions in binary form must reproduce the above copyright
|
||||
+ * notice, this list of conditions and the following disclaimer in the
|
||||
+ * documentation and/or other materials provided with the distribution.
|
||||
+ * 3. The names of the above-listed copyright holders may not be used
|
||||
+ * to endorse or promote products derived from this software without
|
||||
+ * specific prior written permission.
|
||||
+ *
|
||||
+ * ALTERNATIVELY, this software may be distributed under the terms of the
|
||||
+ * GNU General Public License ("GPL") version 2, as published by the Free
|
||||
+ * Software Foundation.
|
||||
+ *
|
||||
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+ */
|
||||
+
|
||||
+#ifndef VCHIQ_KILLABLE_H
|
||||
+#define VCHIQ_KILLABLE_H
|
||||
+
|
||||
+#include <linux/mutex.h>
|
||||
+#include <linux/semaphore.h>
|
||||
+
|
||||
+#define SHUTDOWN_SIGS (sigmask(SIGKILL) | sigmask(SIGINT) | sigmask(SIGQUIT))
|
||||
+
|
||||
+static inline int __must_check down_interruptible_killable(struct semaphore *sem)
|
||||
+{
|
||||
+ /* Allow interception of killable signals only. We don't want to be interrupted by harmless signals like SIGALRM */
|
||||
+ int ret;
|
||||
+ sigset_t blocked, oldset;
|
||||
+ siginitsetinv(&blocked, SHUTDOWN_SIGS);
|
||||
+ sigprocmask(SIG_SETMASK, &blocked, &oldset);
|
||||
+ ret = down_interruptible(sem);
|
||||
+ sigprocmask(SIG_SETMASK, &oldset, NULL);
|
||||
+ return ret;
|
||||
+}
|
||||
+#define down_interruptible down_interruptible_killable
|
||||
+
|
||||
+
|
||||
+static inline int __must_check mutex_lock_interruptible_killable(struct mutex *lock)
|
||||
+{
|
||||
+ /* Allow interception of killable signals only. We don't want to be interrupted by harmless signals like SIGALRM */
|
||||
+ int ret;
|
||||
+ sigset_t blocked, oldset;
|
||||
+ siginitsetinv(&blocked, SHUTDOWN_SIGS);
|
||||
+ sigprocmask(SIG_SETMASK, &blocked, &oldset);
|
||||
+ ret = mutex_lock_interruptible(lock);
|
||||
+ sigprocmask(SIG_SETMASK, &oldset, NULL);
|
||||
+ return ret;
|
||||
+}
|
||||
+#define mutex_lock_interruptible mutex_lock_interruptible_killable
|
||||
+
|
||||
+#endif
|
||||
--- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.c
|
||||
+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.c
|
||||
@@ -32,6 +32,7 @@
|
||||
*/
|
||||
|
||||
#include "vchiq_util.h"
|
||||
+#include "vchiq_killable.h"
|
||||
|
||||
static inline int is_pow2(int i)
|
||||
{
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,471 +0,0 @@
|
|||
From 00d2b7bf3cd58d9735f103ff4cc6982b7dc927fe Mon Sep 17 00:00:00 2001
|
||||
From: popcornmix <popcornmix@gmail.com>
|
||||
Date: Fri, 26 Apr 2013 10:08:31 -0700
|
||||
Subject: [PATCH 10/54] alsa: add mmap support and some cleanups to bcm2835
|
||||
ALSA driver
|
||||
|
||||
---
|
||||
sound/arm/bcm2835-pcm.c | 69 ++++++++++++++++++++++--------------
|
||||
sound/arm/bcm2835-vchiq.c | 89 +++++++++++++++++++++++++++++++++--------------
|
||||
sound/arm/bcm2835.c | 34 +++++++++---------
|
||||
sound/arm/bcm2835.h | 2 ++
|
||||
4 files changed, 124 insertions(+), 70 deletions(-)
|
||||
|
||||
--- a/sound/arm/bcm2835-pcm.c
|
||||
+++ b/sound/arm/bcm2835-pcm.c
|
||||
@@ -19,7 +19,8 @@
|
||||
|
||||
/* hardware definition */
|
||||
static struct snd_pcm_hardware snd_bcm2835_playback_hw = {
|
||||
- .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER),
|
||||
+ .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
|
||||
.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
|
||||
.rate_min = 8000,
|
||||
@@ -251,6 +252,12 @@ static int snd_bcm2835_pcm_prepare(struc
|
||||
|
||||
audio_info(" .. IN\n");
|
||||
|
||||
+ memset(&alsa_stream->pcm_indirect, 0, sizeof(alsa_stream->pcm_indirect));
|
||||
+
|
||||
+ alsa_stream->pcm_indirect.hw_buffer_size =
|
||||
+ alsa_stream->pcm_indirect.sw_buffer_size =
|
||||
+ snd_pcm_lib_buffer_bytes(substream);
|
||||
+
|
||||
alsa_stream->buffer_size = snd_pcm_lib_buffer_bytes(substream);
|
||||
alsa_stream->period_size = snd_pcm_lib_period_bytes(substream);
|
||||
alsa_stream->pos = 0;
|
||||
@@ -263,6 +270,32 @@ static int snd_bcm2835_pcm_prepare(struc
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static void snd_bcm2835_pcm_transfer(struct snd_pcm_substream *substream,
|
||||
+ struct snd_pcm_indirect *rec, size_t bytes)
|
||||
+{
|
||||
+ struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
+ bcm2835_alsa_stream_t *alsa_stream = runtime->private_data;
|
||||
+ void *src = (void *)(substream->runtime->dma_area + rec->sw_data);
|
||||
+ int err;
|
||||
+
|
||||
+ err = bcm2835_audio_write(alsa_stream, bytes, src);
|
||||
+ if (err)
|
||||
+ audio_error(" Failed to transfer to alsa device (%d)\n", err);
|
||||
+
|
||||
+}
|
||||
+
|
||||
+static int snd_bcm2835_pcm_ack(struct snd_pcm_substream *substream)
|
||||
+{
|
||||
+ struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
+ bcm2835_alsa_stream_t *alsa_stream = runtime->private_data;
|
||||
+ struct snd_pcm_indirect *pcm_indirect = &alsa_stream->pcm_indirect;
|
||||
+
|
||||
+ pcm_indirect->hw_queue_size = runtime->hw.buffer_bytes_max;
|
||||
+ snd_pcm_indirect_playback_transfer(substream, pcm_indirect,
|
||||
+ snd_bcm2835_pcm_transfer);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
/* trigger callback */
|
||||
static int snd_bcm2835_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
@@ -279,6 +312,11 @@ static int snd_bcm2835_pcm_trigger(struc
|
||||
if (!alsa_stream->running) {
|
||||
err = bcm2835_audio_start(alsa_stream);
|
||||
if (err == 0) {
|
||||
+ alsa_stream->pcm_indirect.hw_io =
|
||||
+ alsa_stream->pcm_indirect.hw_data =
|
||||
+ bytes_to_frames(runtime,
|
||||
+ alsa_stream->pos);
|
||||
+ substream->ops->ack(substream);
|
||||
alsa_stream->running = 1;
|
||||
alsa_stream->draining = 1;
|
||||
} else {
|
||||
@@ -327,30 +365,9 @@ snd_bcm2835_pcm_pointer(struct snd_pcm_s
|
||||
alsa_stream->pos);
|
||||
|
||||
audio_info(" .. OUT\n");
|
||||
- return bytes_to_frames(runtime, alsa_stream->pos);
|
||||
-}
|
||||
-
|
||||
-static int snd_bcm2835_pcm_copy(struct snd_pcm_substream *substream,
|
||||
- int channel, snd_pcm_uframes_t pos, void *src,
|
||||
- snd_pcm_uframes_t count)
|
||||
-{
|
||||
- int ret;
|
||||
- struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
- bcm2835_alsa_stream_t *alsa_stream = runtime->private_data;
|
||||
-
|
||||
- audio_info(" .. IN\n");
|
||||
- audio_debug("copy.......... (%d) hwptr=%d appl=%d pos=%d\n",
|
||||
- frames_to_bytes(runtime, count), frames_to_bytes(runtime,
|
||||
- runtime->
|
||||
- status->
|
||||
- hw_ptr),
|
||||
- frames_to_bytes(runtime, runtime->control->appl_ptr),
|
||||
- alsa_stream->pos);
|
||||
- ret =
|
||||
- bcm2835_audio_write(alsa_stream, frames_to_bytes(runtime, count),
|
||||
- src);
|
||||
- audio_info(" .. OUT\n");
|
||||
- return ret;
|
||||
+ return snd_pcm_indirect_playback_pointer(substream,
|
||||
+ &alsa_stream->pcm_indirect,
|
||||
+ alsa_stream->pos);
|
||||
}
|
||||
|
||||
static int snd_bcm2835_pcm_lib_ioctl(struct snd_pcm_substream *substream,
|
||||
@@ -372,7 +389,7 @@ static struct snd_pcm_ops snd_bcm2835_pl
|
||||
.prepare = snd_bcm2835_pcm_prepare,
|
||||
.trigger = snd_bcm2835_pcm_trigger,
|
||||
.pointer = snd_bcm2835_pcm_pointer,
|
||||
- .copy = snd_bcm2835_pcm_copy,
|
||||
+ .ack = snd_bcm2835_pcm_ack,
|
||||
};
|
||||
|
||||
/* create a pcm device */
|
||||
--- a/sound/arm/bcm2835-vchiq.c
|
||||
+++ b/sound/arm/bcm2835-vchiq.c
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/module.h>
|
||||
+#include <linux/completion.h>
|
||||
|
||||
#include "bcm2835.h"
|
||||
|
||||
@@ -37,6 +38,10 @@
|
||||
|
||||
/* ---- Private Constants and Types ------------------------------------------ */
|
||||
|
||||
+#define BCM2835_AUDIO_STOP 0
|
||||
+#define BCM2835_AUDIO_START 1
|
||||
+#define BCM2835_AUDIO_WRITE 2
|
||||
+
|
||||
/* Logging macros (for remapping to other logging mechanisms, i.e., printf) */
|
||||
#ifdef AUDIO_DEBUG_ENABLE
|
||||
#define LOG_ERR( fmt, arg... ) pr_err( "%s:%d " fmt, __func__, __LINE__, ##arg)
|
||||
@@ -53,7 +58,7 @@
|
||||
typedef struct opaque_AUDIO_INSTANCE_T {
|
||||
uint32_t num_connections;
|
||||
VCHI_SERVICE_HANDLE_T vchi_handle[VCHI_MAX_NUM_CONNECTIONS];
|
||||
- struct semaphore msg_avail_event;
|
||||
+ struct completion msg_avail_comp;
|
||||
struct mutex vchi_mutex;
|
||||
bcm2835_alsa_stream_t *alsa_stream;
|
||||
int32_t result;
|
||||
@@ -70,27 +75,35 @@ bool force_bulk = false;
|
||||
|
||||
static int bcm2835_audio_stop_worker(bcm2835_alsa_stream_t * alsa_stream);
|
||||
static int bcm2835_audio_start_worker(bcm2835_alsa_stream_t * alsa_stream);
|
||||
+static int bcm2835_audio_write_worker(bcm2835_alsa_stream_t *alsa_stream,
|
||||
+ uint32_t count, void *src);
|
||||
|
||||
typedef struct {
|
||||
struct work_struct my_work;
|
||||
bcm2835_alsa_stream_t *alsa_stream;
|
||||
- int x;
|
||||
+ int cmd;
|
||||
+ void *src;
|
||||
+ uint32_t count;
|
||||
} my_work_t;
|
||||
|
||||
static void my_wq_function(struct work_struct *work)
|
||||
{
|
||||
my_work_t *w = (my_work_t *) work;
|
||||
int ret = -9;
|
||||
- LOG_DBG(" .. IN %p:%d\n", w->alsa_stream, w->x);
|
||||
- switch (w->x) {
|
||||
- case 1:
|
||||
+ LOG_DBG(" .. IN %p:%d\n", w->alsa_stream, w->cmd);
|
||||
+ switch (w->cmd) {
|
||||
+ case BCM2835_AUDIO_START:
|
||||
ret = bcm2835_audio_start_worker(w->alsa_stream);
|
||||
break;
|
||||
- case 2:
|
||||
+ case BCM2835_AUDIO_STOP:
|
||||
ret = bcm2835_audio_stop_worker(w->alsa_stream);
|
||||
break;
|
||||
+ case BCM2835_AUDIO_WRITE:
|
||||
+ ret = bcm2835_audio_write_worker(w->alsa_stream, w->count,
|
||||
+ w->src);
|
||||
+ break;
|
||||
default:
|
||||
- LOG_ERR(" Unexpected work: %p:%d\n", w->alsa_stream, w->x);
|
||||
+ LOG_ERR(" Unexpected work: %p:%d\n", w->alsa_stream, w->cmd);
|
||||
break;
|
||||
}
|
||||
kfree((void *)work);
|
||||
@@ -107,7 +120,7 @@ int bcm2835_audio_start(bcm2835_alsa_str
|
||||
if (work) {
|
||||
INIT_WORK((struct work_struct *)work, my_wq_function);
|
||||
work->alsa_stream = alsa_stream;
|
||||
- work->x = 1;
|
||||
+ work->cmd = BCM2835_AUDIO_START;
|
||||
if (queue_work
|
||||
(alsa_stream->my_wq, (struct work_struct *)work))
|
||||
ret = 0;
|
||||
@@ -128,7 +141,31 @@ int bcm2835_audio_stop(bcm2835_alsa_stre
|
||||
if (work) {
|
||||
INIT_WORK((struct work_struct *)work, my_wq_function);
|
||||
work->alsa_stream = alsa_stream;
|
||||
- work->x = 2;
|
||||
+ work->cmd = BCM2835_AUDIO_STOP;
|
||||
+ if (queue_work
|
||||
+ (alsa_stream->my_wq, (struct work_struct *)work))
|
||||
+ ret = 0;
|
||||
+ } else
|
||||
+ LOG_ERR(" .. Error: NULL work kmalloc\n");
|
||||
+ }
|
||||
+ LOG_DBG(" .. OUT %d\n", ret);
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+int bcm2835_audio_write(bcm2835_alsa_stream_t *alsa_stream,
|
||||
+ uint32_t count, void *src)
|
||||
+{
|
||||
+ int ret = -1;
|
||||
+ LOG_DBG(" .. IN\n");
|
||||
+ if (alsa_stream->my_wq) {
|
||||
+ my_work_t *work = kmalloc(sizeof(my_work_t), GFP_ATOMIC);
|
||||
+ /*--- Queue some work (item 1) ---*/
|
||||
+ if (work) {
|
||||
+ INIT_WORK((struct work_struct *)work, my_wq_function);
|
||||
+ work->alsa_stream = alsa_stream;
|
||||
+ work->cmd = BCM2835_AUDIO_WRITE;
|
||||
+ work->src = src;
|
||||
+ work->count = count;
|
||||
if (queue_work
|
||||
(alsa_stream->my_wq, (struct work_struct *)work))
|
||||
ret = 0;
|
||||
@@ -178,7 +215,7 @@ static void audio_vchi_callback(void *pa
|
||||
(" .. instance=%p, m.type=VC_AUDIO_MSG_TYPE_RESULT, success=%d\n",
|
||||
instance, m.u.result.success);
|
||||
instance->result = m.u.result.success;
|
||||
- up(&instance->msg_avail_event);
|
||||
+ complete(&instance->msg_avail_comp);
|
||||
} else if (m.type == VC_AUDIO_MSG_TYPE_COMPLETE) {
|
||||
irq_handler_t callback = (irq_handler_t) m.u.complete.callback;
|
||||
LOG_DBG
|
||||
@@ -435,8 +472,8 @@ static int bcm2835_audio_set_ctls_chan(b
|
||||
m.u.control.dest = chip->dest;
|
||||
m.u.control.volume = chip->volume;
|
||||
|
||||
- /* Create the message available event */
|
||||
- sema_init(&instance->msg_avail_event, 0);
|
||||
+ /* Create the message available completion */
|
||||
+ init_completion(&instance->msg_avail_comp);
|
||||
|
||||
/* Send the message to the videocore */
|
||||
success = vchi_msg_queue(instance->vchi_handle[0],
|
||||
@@ -452,11 +489,10 @@ static int bcm2835_audio_set_ctls_chan(b
|
||||
}
|
||||
|
||||
/* We are expecting a reply from the videocore */
|
||||
- if (down_interruptible(&instance->msg_avail_event)) {
|
||||
+ ret = wait_for_completion_interruptible(&instance->msg_avail_comp);
|
||||
+ if (ret) {
|
||||
LOG_ERR("%s: failed on waiting for event (status=%d)\n",
|
||||
__func__, success);
|
||||
-
|
||||
- ret = -1;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
@@ -539,8 +575,8 @@ int bcm2835_audio_set_params(bcm2835_als
|
||||
m.u.config.samplerate = samplerate;
|
||||
m.u.config.bps = bps;
|
||||
|
||||
- /* Create the message available event */
|
||||
- sema_init(&instance->msg_avail_event, 0);
|
||||
+ /* Create the message available completion */
|
||||
+ init_completion(&instance->msg_avail_comp);
|
||||
|
||||
/* Send the message to the videocore */
|
||||
success = vchi_msg_queue(instance->vchi_handle[0],
|
||||
@@ -556,11 +592,10 @@ int bcm2835_audio_set_params(bcm2835_als
|
||||
}
|
||||
|
||||
/* We are expecting a reply from the videocore */
|
||||
- if (down_interruptible(&instance->msg_avail_event)) {
|
||||
+ ret = wait_for_completion_interruptible(&instance->msg_avail_comp);
|
||||
+ if (ret) {
|
||||
LOG_ERR("%s: failed on waiting for event (status=%d)\n",
|
||||
__func__, success);
|
||||
-
|
||||
- ret = -1;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
@@ -688,8 +723,8 @@ int bcm2835_audio_close(bcm2835_alsa_str
|
||||
|
||||
m.type = VC_AUDIO_MSG_TYPE_CLOSE;
|
||||
|
||||
- /* Create the message available event */
|
||||
- sema_init(&instance->msg_avail_event, 0);
|
||||
+ /* Create the message available completion */
|
||||
+ init_completion(&instance->msg_avail_comp);
|
||||
|
||||
/* Send the message to the videocore */
|
||||
success = vchi_msg_queue(instance->vchi_handle[0],
|
||||
@@ -702,11 +737,11 @@ int bcm2835_audio_close(bcm2835_alsa_str
|
||||
ret = -1;
|
||||
goto unlock;
|
||||
}
|
||||
- if (down_interruptible(&instance->msg_avail_event)) {
|
||||
+
|
||||
+ ret = wait_for_completion_interruptible(&instance->msg_avail_comp);
|
||||
+ if (ret) {
|
||||
LOG_ERR("%s: failed on waiting for event (status=%d)",
|
||||
__func__, success);
|
||||
-
|
||||
- ret = -1;
|
||||
goto unlock;
|
||||
}
|
||||
if (instance->result != 0) {
|
||||
@@ -732,8 +767,8 @@ unlock:
|
||||
return ret;
|
||||
}
|
||||
|
||||
-int bcm2835_audio_write(bcm2835_alsa_stream_t * alsa_stream, uint32_t count,
|
||||
- void *src)
|
||||
+int bcm2835_audio_write_worker(bcm2835_alsa_stream_t *alsa_stream,
|
||||
+ uint32_t count, void *src)
|
||||
{
|
||||
VC_AUDIO_MSG_T m;
|
||||
AUDIO_INSTANCE_T *instance = alsa_stream->instance;
|
||||
--- a/sound/arm/bcm2835.c
|
||||
+++ b/sound/arm/bcm2835.c
|
||||
@@ -110,20 +110,20 @@ static int snd_bcm2835_alsa_probe(struct
|
||||
|
||||
err = snd_bcm2835_create(g_card, pdev, &chip);
|
||||
if (err < 0) {
|
||||
- printk(KERN_ERR "Failed to create bcm2835 chip\n");
|
||||
+ dev_err(&pdev->dev, "Failed to create bcm2835 chip\n");
|
||||
goto out_bcm2835_create;
|
||||
}
|
||||
|
||||
g_chip = chip;
|
||||
err = snd_bcm2835_new_pcm(chip);
|
||||
if (err < 0) {
|
||||
- printk(KERN_ERR "Failed to create new BCM2835 pcm device\n");
|
||||
+ dev_err(&pdev->dev, "Failed to create new BCM2835 pcm device\n");
|
||||
goto out_bcm2835_new_pcm;
|
||||
}
|
||||
|
||||
err = snd_bcm2835_new_ctl(chip);
|
||||
if (err < 0) {
|
||||
- printk(KERN_ERR "Failed to create new BCM2835 ctl\n");
|
||||
+ dev_err(&pdev->dev, "Failed to create new BCM2835 ctl\n");
|
||||
goto out_bcm2835_new_ctl;
|
||||
}
|
||||
|
||||
@@ -139,14 +139,14 @@ add_register_map:
|
||||
if (dev == 0) {
|
||||
err = snd_card_register(card);
|
||||
if (err < 0) {
|
||||
- printk(KERN_ERR
|
||||
- "Failed to register bcm2835 ALSA card \n");
|
||||
+ dev_err(&pdev->dev,
|
||||
+ "Failed to register bcm2835 ALSA card \n");
|
||||
goto out_card_register;
|
||||
}
|
||||
platform_set_drvdata(pdev, card);
|
||||
- printk(KERN_INFO "bcm2835 ALSA card created!\n");
|
||||
+ audio_info("bcm2835 ALSA card created!\n");
|
||||
} else {
|
||||
- printk(KERN_INFO "bcm2835 ALSA chip created!\n");
|
||||
+ audio_info("bcm2835 ALSA chip created!\n");
|
||||
platform_set_drvdata(pdev, (void *)dev);
|
||||
}
|
||||
|
||||
@@ -160,11 +160,11 @@ out_bcm2835_new_pcm:
|
||||
out_bcm2835_create:
|
||||
BUG_ON(!g_card);
|
||||
if (snd_card_free(g_card))
|
||||
- printk(KERN_ERR "Failed to free Registered alsa card\n");
|
||||
+ dev_err(&pdev->dev, "Failed to free Registered alsa card\n");
|
||||
g_card = NULL;
|
||||
out:
|
||||
dev = SNDRV_CARDS; /* stop more avail_substreams from being probed */
|
||||
- printk(KERN_ERR "BCM2835 ALSA Probe failed !!\n");
|
||||
+ dev_err(&pdev->dev, "BCM2835 ALSA Probe failed !!\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -326,49 +326,49 @@ static int bcm2835_alsa_device_init(void
|
||||
int err;
|
||||
err = platform_driver_register(&bcm2835_alsa0_driver);
|
||||
if (err) {
|
||||
- printk("Error registering bcm2835_alsa0_driver %d .\n", err);
|
||||
+ pr_err("Error registering bcm2835_alsa0_driver %d .\n", err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = platform_driver_register(&bcm2835_alsa1_driver);
|
||||
if (err) {
|
||||
- printk("Error registering bcm2835_alsa1_driver %d .\n", err);
|
||||
+ pr_err("Error registering bcm2835_alsa0_driver %d .\n", err);
|
||||
goto unregister_0;
|
||||
}
|
||||
|
||||
err = platform_driver_register(&bcm2835_alsa2_driver);
|
||||
if (err) {
|
||||
- printk("Error registering bcm2835_alsa2_driver %d .\n", err);
|
||||
+ pr_err("Error registering bcm2835_alsa0_driver %d .\n", err);
|
||||
goto unregister_1;
|
||||
}
|
||||
|
||||
err = platform_driver_register(&bcm2835_alsa3_driver);
|
||||
if (err) {
|
||||
- printk("Error registering bcm2835_alsa3_driver %d .\n", err);
|
||||
+ pr_err("Error registering bcm2835_alsa0_driver %d .\n", err);
|
||||
goto unregister_2;
|
||||
}
|
||||
|
||||
err = platform_driver_register(&bcm2835_alsa4_driver);
|
||||
if (err) {
|
||||
- printk("Error registering bcm2835_alsa4_driver %d .\n", err);
|
||||
+ pr_err("Error registering bcm2835_alsa0_driver %d .\n", err);
|
||||
goto unregister_3;
|
||||
}
|
||||
|
||||
err = platform_driver_register(&bcm2835_alsa5_driver);
|
||||
if (err) {
|
||||
- printk("Error registering bcm2835_alsa5_driver %d .\n", err);
|
||||
+ pr_err("Error registering bcm2835_alsa0_driver %d .\n", err);
|
||||
goto unregister_4;
|
||||
}
|
||||
|
||||
err = platform_driver_register(&bcm2835_alsa6_driver);
|
||||
if (err) {
|
||||
- printk("Error registering bcm2835_alsa6_driver %d .\n", err);
|
||||
+ pr_err("Error registering bcm2835_alsa0_driver %d .\n", err);
|
||||
goto unregister_5;
|
||||
}
|
||||
|
||||
err = platform_driver_register(&bcm2835_alsa7_driver);
|
||||
if (err) {
|
||||
- printk("Error registering bcm2835_alsa7_driver %d .\n", err);
|
||||
+ pr_err("Error registering bcm2835_alsa0_driver %d .\n", err);
|
||||
goto unregister_6;
|
||||
}
|
||||
|
||||
--- a/sound/arm/bcm2835.h
|
||||
+++ b/sound/arm/bcm2835.h
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <sound/initval.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
+#include <sound/pcm-indirect.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
/*
|
||||
@@ -110,6 +111,7 @@ typedef struct bcm2835_chip {
|
||||
typedef struct bcm2835_alsa_stream {
|
||||
bcm2835_chip_t *chip;
|
||||
struct snd_pcm_substream *substream;
|
||||
+ struct snd_pcm_indirect pcm_indirect;
|
||||
|
||||
struct semaphore buffers_update_sem;
|
||||
struct semaphore control_sem;
|
|
@ -1,171 +0,0 @@
|
|||
From 2bae9a662d169ecb7d3e1c39ebbcbc80b7dc845f Mon Sep 17 00:00:00 2001
|
||||
From: popcornmix <popcornmix@gmail.com>
|
||||
Date: Wed, 3 Jul 2013 00:51:55 +0100
|
||||
Subject: [PATCH 11/54] Add hwrng (hardware random number generator) driver
|
||||
|
||||
---
|
||||
arch/arm/mach-bcm2708/include/mach/platform.h | 1 +
|
||||
drivers/char/hw_random/Kconfig | 11 +++
|
||||
drivers/char/hw_random/Makefile | 1 +
|
||||
drivers/char/hw_random/bcm2708-rng.c | 117 ++++++++++++++++++++++++++
|
||||
4 files changed, 130 insertions(+)
|
||||
create mode 100755 drivers/char/hw_random/bcm2708-rng.c
|
||||
|
||||
--- a/arch/arm/mach-bcm2708/include/mach/platform.h
|
||||
+++ b/arch/arm/mach-bcm2708/include/mach/platform.h
|
||||
@@ -60,6 +60,7 @@
|
||||
#define DMA_BASE (BCM2708_PERI_BASE + 0x7000) /* DMA controller */
|
||||
#define ARM_BASE (BCM2708_PERI_BASE + 0xB000) /* BCM2708 ARM control block */
|
||||
#define PM_BASE (BCM2708_PERI_BASE + 0x100000) /* Power Management, Reset controller and Watchdog registers */
|
||||
+#define RNG_BASE (BCM2708_PERI_BASE + 0x104000) /* Hardware RNG */
|
||||
#define GPIO_BASE (BCM2708_PERI_BASE + 0x200000) /* GPIO */
|
||||
#define UART0_BASE (BCM2708_PERI_BASE + 0x201000) /* Uart 0 */
|
||||
#define MMCI0_BASE (BCM2708_PERI_BASE + 0x202000) /* MMC interface */
|
||||
--- a/drivers/char/hw_random/Kconfig
|
||||
+++ b/drivers/char/hw_random/Kconfig
|
||||
@@ -341,6 +341,17 @@ config HW_RANDOM_TPM
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
+config HW_RANDOM_BCM2708
|
||||
+ tristate "BCM2708 generic true random number generator support"
|
||||
+ depends on HW_RANDOM && ARCH_BCM2708
|
||||
+ ---help---
|
||||
+ This driver provides the kernel-side support for the BCM2708 hardware.
|
||||
+
|
||||
+ To compile this driver as a module, choose M here: the
|
||||
+ module will be called bcm2708-rng.
|
||||
+
|
||||
+ If unsure, say N.
|
||||
+
|
||||
config HW_RANDOM_MSM
|
||||
tristate "Qualcomm MSM Random Number Generator support"
|
||||
depends on HW_RANDOM && ARCH_MSM
|
||||
--- a/drivers/char/hw_random/Makefile
|
||||
+++ b/drivers/char/hw_random/Makefile
|
||||
@@ -29,4 +29,5 @@ obj-$(CONFIG_HW_RANDOM_POWERNV) += power
|
||||
obj-$(CONFIG_HW_RANDOM_EXYNOS) += exynos-rng.o
|
||||
obj-$(CONFIG_HW_RANDOM_TPM) += tpm-rng.o
|
||||
obj-$(CONFIG_HW_RANDOM_BCM2835) += bcm2835-rng.o
|
||||
+obj-$(CONFIG_HW_RANDOM_BCM2708) += bcm2708-rng.o
|
||||
obj-$(CONFIG_HW_RANDOM_MSM) += msm-rng.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/char/hw_random/bcm2708-rng.c
|
||||
@@ -0,0 +1,117 @@
|
||||
+/**
|
||||
+ * Copyright (c) 2010-2012 Broadcom. All rights reserved.
|
||||
+ *
|
||||
+ * Redistribution and use in source and binary forms, with or without
|
||||
+ * modification, are permitted provided that the following conditions
|
||||
+ * are met:
|
||||
+ * 1. Redistributions of source code must retain the above copyright
|
||||
+ * notice, this list of conditions, and the following disclaimer,
|
||||
+ * without modification.
|
||||
+ * 2. Redistributions in binary form must reproduce the above copyright
|
||||
+ * notice, this list of conditions and the following disclaimer in the
|
||||
+ * documentation and/or other materials provided with the distribution.
|
||||
+ * 3. The names of the above-listed copyright holders may not be used
|
||||
+ * to endorse or promote products derived from this software without
|
||||
+ * specific prior written permission.
|
||||
+ *
|
||||
+ * ALTERNATIVELY, this software may be distributed under the terms of the
|
||||
+ * GNU General Public License ("GPL") version 2, as published by the Free
|
||||
+ * Software Foundation.
|
||||
+ *
|
||||
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/hw_random.h>
|
||||
+#include <linux/printk.h>
|
||||
+
|
||||
+#include <asm/io.h>
|
||||
+#include <mach/hardware.h>
|
||||
+#include <mach/platform.h>
|
||||
+
|
||||
+#define RNG_CTRL (0x0)
|
||||
+#define RNG_STATUS (0x4)
|
||||
+#define RNG_DATA (0x8)
|
||||
+#define RNG_FF_THRESHOLD (0xc)
|
||||
+
|
||||
+/* enable rng */
|
||||
+#define RNG_RBGEN 0x1
|
||||
+/* double speed, less random mode */
|
||||
+#define RNG_RBG2X 0x2
|
||||
+
|
||||
+/* the initial numbers generated are "less random" so will be discarded */
|
||||
+#define RNG_WARMUP_COUNT 0x40000
|
||||
+
|
||||
+static int bcm2708_rng_data_read(struct hwrng *rng, u32 *buffer)
|
||||
+{
|
||||
+ void __iomem *rng_base = (void __iomem *)rng->priv;
|
||||
+ unsigned words;
|
||||
+ /* wait for a random number to be in fifo */
|
||||
+ do {
|
||||
+ words = __raw_readl(rng_base + RNG_STATUS)>>24;
|
||||
+ }
|
||||
+ while (words == 0);
|
||||
+ /* read the random number */
|
||||
+ *buffer = __raw_readl(rng_base + RNG_DATA);
|
||||
+ return 4;
|
||||
+}
|
||||
+
|
||||
+static struct hwrng bcm2708_rng_ops = {
|
||||
+ .name = "bcm2708",
|
||||
+ .data_read = bcm2708_rng_data_read,
|
||||
+};
|
||||
+
|
||||
+static int __init bcm2708_rng_init(void)
|
||||
+{
|
||||
+ void __iomem *rng_base;
|
||||
+ int err;
|
||||
+
|
||||
+ /* map peripheral */
|
||||
+ rng_base = ioremap(RNG_BASE, 0x10);
|
||||
+ pr_info("bcm2708_rng_init=%p\n", rng_base);
|
||||
+ if (!rng_base) {
|
||||
+ pr_err("bcm2708_rng_init failed to ioremap\n");
|
||||
+ return -ENOMEM;
|
||||
+ }
|
||||
+ bcm2708_rng_ops.priv = (unsigned long)rng_base;
|
||||
+ /* register driver */
|
||||
+ err = hwrng_register(&bcm2708_rng_ops);
|
||||
+ if (err) {
|
||||
+ pr_err("bcm2708_rng_init hwrng_register()=%d\n", err);
|
||||
+ iounmap(rng_base);
|
||||
+ } else {
|
||||
+ /* set warm-up count & enable */
|
||||
+ __raw_writel(RNG_WARMUP_COUNT, rng_base + RNG_STATUS);
|
||||
+ __raw_writel(RNG_RBGEN, rng_base + RNG_CTRL);
|
||||
+ }
|
||||
+ return err;
|
||||
+}
|
||||
+
|
||||
+static void __exit bcm2708_rng_exit(void)
|
||||
+{
|
||||
+ void __iomem *rng_base = (void __iomem *)bcm2708_rng_ops.priv;
|
||||
+ pr_info("bcm2708_rng_exit\n");
|
||||
+ /* disable rng hardware */
|
||||
+ __raw_writel(0, rng_base + RNG_CTRL);
|
||||
+ /* unregister driver */
|
||||
+ hwrng_unregister(&bcm2708_rng_ops);
|
||||
+ iounmap(rng_base);
|
||||
+}
|
||||
+
|
||||
+module_init(bcm2708_rng_init);
|
||||
+module_exit(bcm2708_rng_exit);
|
||||
+
|
||||
+MODULE_DESCRIPTION("BCM2708 H/W Random Number Generator (RNG) driver");
|
||||
+MODULE_LICENSE("GPL and additional rights");
|
|
@ -1,737 +0,0 @@
|
|||
From 04439327ad7c2afd72b6fe8c43c922b892dff7c3 Mon Sep 17 00:00:00 2001
|
||||
From: Aron Szabo <aron@aron.ws>
|
||||
Date: Sat, 16 Jun 2012 12:15:55 +0200
|
||||
Subject: [PATCH 12/54] lirc: added support for RaspberryPi GPIO
|
||||
|
||||
lirc_rpi: Use read_current_timer to determine transmitter delay. Thanks to jjmz and others
|
||||
See: https://github.com/raspberrypi/linux/issues/525
|
||||
---
|
||||
drivers/staging/media/lirc/Kconfig | 6 +
|
||||
drivers/staging/media/lirc/Makefile | 1 +
|
||||
drivers/staging/media/lirc/lirc_rpi.c | 695 ++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 702 insertions(+)
|
||||
create mode 100644 drivers/staging/media/lirc/lirc_rpi.c
|
||||
|
||||
--- a/drivers/staging/media/lirc/Kconfig
|
||||
+++ b/drivers/staging/media/lirc/Kconfig
|
||||
@@ -38,6 +38,12 @@ config LIRC_PARALLEL
|
||||
help
|
||||
Driver for Homebrew Parallel Port Receivers
|
||||
|
||||
+config LIRC_RPI
|
||||
+ tristate "Homebrew GPIO Port Receiver/Transmitter for the RaspberryPi"
|
||||
+ depends on LIRC
|
||||
+ help
|
||||
+ Driver for Homebrew GPIO Port Receiver/Transmitter for the RaspberryPi
|
||||
+
|
||||
config LIRC_SASEM
|
||||
tristate "Sasem USB IR Remote"
|
||||
depends on LIRC && USB
|
||||
--- a/drivers/staging/media/lirc/Makefile
|
||||
+++ b/drivers/staging/media/lirc/Makefile
|
||||
@@ -7,6 +7,7 @@ obj-$(CONFIG_LIRC_BT829) += lirc_bt829.o
|
||||
obj-$(CONFIG_LIRC_IGORPLUGUSB) += lirc_igorplugusb.o
|
||||
obj-$(CONFIG_LIRC_IMON) += lirc_imon.o
|
||||
obj-$(CONFIG_LIRC_PARALLEL) += lirc_parallel.o
|
||||
+obj-$(CONFIG_LIRC_RPI) += lirc_rpi.o
|
||||
obj-$(CONFIG_LIRC_SASEM) += lirc_sasem.o
|
||||
obj-$(CONFIG_LIRC_SERIAL) += lirc_serial.o
|
||||
obj-$(CONFIG_LIRC_SIR) += lirc_sir.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/staging/media/lirc/lirc_rpi.c
|
||||
@@ -0,0 +1,695 @@
|
||||
+/*
|
||||
+ * lirc_rpi.c
|
||||
+ *
|
||||
+ * lirc_rpi - Device driver that records pulse- and pause-lengths
|
||||
+ * (space-lengths) (just like the lirc_serial driver does)
|
||||
+ * between GPIO interrupt events on the Raspberry Pi.
|
||||
+ * Lots of code has been taken from the lirc_serial module,
|
||||
+ * so I would like say thanks to the authors.
|
||||
+ *
|
||||
+ * Copyright (C) 2012 Aron Robert Szabo <aron@reon.hu>,
|
||||
+ * Michael Bishop <cleverca22@gmail.com>
|
||||
+ * This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License as published by
|
||||
+ * the Free Software Foundation; either version 2 of the License, or
|
||||
+ * (at your option) any later version.
|
||||
+ *
|
||||
+ * 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.
|
||||
+ *
|
||||
+ * You should have received a copy of the GNU General Public License
|
||||
+ * along with this program; if not, write to the Free Software
|
||||
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
+ */
|
||||
+
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/errno.h>
|
||||
+#include <linux/interrupt.h>
|
||||
+#include <linux/sched.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/time.h>
|
||||
+#include <linux/timex.h>
|
||||
+#include <linux/string.h>
|
||||
+#include <linux/delay.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/irq.h>
|
||||
+#include <linux/spinlock.h>
|
||||
+#include <media/lirc.h>
|
||||
+#include <media/lirc_dev.h>
|
||||
+#include <linux/gpio.h>
|
||||
+
|
||||
+#define LIRC_DRIVER_NAME "lirc_rpi"
|
||||
+#define RBUF_LEN 256
|
||||
+#define LIRC_TRANSMITTER_LATENCY 50
|
||||
+
|
||||
+#ifndef MAX_UDELAY_MS
|
||||
+#define MAX_UDELAY_US 5000
|
||||
+#else
|
||||
+#define MAX_UDELAY_US (MAX_UDELAY_MS*1000)
|
||||
+#endif
|
||||
+
|
||||
+#define dprintk(fmt, args...) \
|
||||
+ do { \
|
||||
+ if (debug) \
|
||||
+ printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \
|
||||
+ fmt, ## args); \
|
||||
+ } while (0)
|
||||
+
|
||||
+/* module parameters */
|
||||
+
|
||||
+/* set the default GPIO input pin */
|
||||
+static int gpio_in_pin = 18;
|
||||
+/* set the default GPIO output pin */
|
||||
+static int gpio_out_pin = 17;
|
||||
+/* enable debugging messages */
|
||||
+static bool debug;
|
||||
+/* -1 = auto, 0 = active high, 1 = active low */
|
||||
+static int sense = -1;
|
||||
+/* use softcarrier by default */
|
||||
+static bool softcarrier = 1;
|
||||
+/* 0 = do not invert output, 1 = invert output */
|
||||
+static bool invert = 0;
|
||||
+
|
||||
+struct gpio_chip *gpiochip;
|
||||
+struct irq_chip *irqchip;
|
||||
+struct irq_data *irqdata;
|
||||
+
|
||||
+/* forward declarations */
|
||||
+static long send_pulse(unsigned long length);
|
||||
+static void send_space(long length);
|
||||
+static void lirc_rpi_exit(void);
|
||||
+
|
||||
+int valid_gpio_pins[] = { 0, 1, 2, 3, 4, 7, 8, 9, 10, 11, 14, 15, 17, 18, 21,
|
||||
+ 22, 23, 24, 25 ,27, 28, 29, 30, 31 };
|
||||
+
|
||||
+static struct platform_device *lirc_rpi_dev;
|
||||
+static struct timeval lasttv = { 0, 0 };
|
||||
+static struct lirc_buffer rbuf;
|
||||
+static spinlock_t lock;
|
||||
+
|
||||
+/* initialized/set in init_timing_params() */
|
||||
+static unsigned int freq = 38000;
|
||||
+static unsigned int duty_cycle = 50;
|
||||
+static unsigned long period;
|
||||
+static unsigned long pulse_width;
|
||||
+static unsigned long space_width;
|
||||
+
|
||||
+static void safe_udelay(unsigned long usecs)
|
||||
+{
|
||||
+ while (usecs > MAX_UDELAY_US) {
|
||||
+ udelay(MAX_UDELAY_US);
|
||||
+ usecs -= MAX_UDELAY_US;
|
||||
+ }
|
||||
+ udelay(usecs);
|
||||
+}
|
||||
+
|
||||
+static int init_timing_params(unsigned int new_duty_cycle,
|
||||
+ unsigned int new_freq)
|
||||
+{
|
||||
+ if (1000 * 1000000L / new_freq * new_duty_cycle / 100 <=
|
||||
+ LIRC_TRANSMITTER_LATENCY)
|
||||
+ return -EINVAL;
|
||||
+ if (1000 * 1000000L / new_freq * (100 - new_duty_cycle) / 100 <=
|
||||
+ LIRC_TRANSMITTER_LATENCY)
|
||||
+ return -EINVAL;
|
||||
+ duty_cycle = new_duty_cycle;
|
||||
+ freq = new_freq;
|
||||
+ period = 1000 * 1000000L / freq;
|
||||
+ pulse_width = period * duty_cycle / 100;
|
||||
+ space_width = period - pulse_width;
|
||||
+ dprintk("in init_timing_params, freq=%d pulse=%ld, "
|
||||
+ "space=%ld\n", freq, pulse_width, space_width);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static long send_pulse_softcarrier(unsigned long length)
|
||||
+{
|
||||
+ int flag;
|
||||
+ unsigned long actual, target;
|
||||
+ unsigned long actual_us, initial_us, target_us;
|
||||
+
|
||||
+ length *= 1000;
|
||||
+
|
||||
+ actual = 0; target = 0; flag = 0;
|
||||
+ read_current_timer(&actual_us);
|
||||
+
|
||||
+ while (actual < length) {
|
||||
+ if (flag) {
|
||||
+ gpiochip->set(gpiochip, gpio_out_pin, invert);
|
||||
+ target += space_width;
|
||||
+ } else {
|
||||
+ gpiochip->set(gpiochip, gpio_out_pin, !invert);
|
||||
+ target += pulse_width;
|
||||
+ }
|
||||
+ initial_us = actual_us;
|
||||
+ target_us = actual_us + (target - actual) / 1000;
|
||||
+ /*
|
||||
+ * Note - we've checked in ioctl that the pulse/space
|
||||
+ * widths are big enough so that d is > 0
|
||||
+ */
|
||||
+ if ((int)(target_us - actual_us) > 0)
|
||||
+ udelay(target_us - actual_us);
|
||||
+ read_current_timer(&actual_us);
|
||||
+ actual += (actual_us - initial_us) * 1000;
|
||||
+ flag = !flag;
|
||||
+ }
|
||||
+ return (actual-length) / 1000;
|
||||
+}
|
||||
+
|
||||
+static long send_pulse(unsigned long length)
|
||||
+{
|
||||
+ if (length <= 0)
|
||||
+ return 0;
|
||||
+
|
||||
+ if (softcarrier) {
|
||||
+ return send_pulse_softcarrier(length);
|
||||
+ } else {
|
||||
+ gpiochip->set(gpiochip, gpio_out_pin, !invert);
|
||||
+ safe_udelay(length);
|
||||
+ return 0;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void send_space(long length)
|
||||
+{
|
||||
+ gpiochip->set(gpiochip, gpio_out_pin, invert);
|
||||
+ if (length <= 0)
|
||||
+ return;
|
||||
+ safe_udelay(length);
|
||||
+}
|
||||
+
|
||||
+static void rbwrite(int l)
|
||||
+{
|
||||
+ if (lirc_buffer_full(&rbuf)) {
|
||||
+ /* no new signals will be accepted */
|
||||
+ dprintk("Buffer overrun\n");
|
||||
+ return;
|
||||
+ }
|
||||
+ lirc_buffer_write(&rbuf, (void *)&l);
|
||||
+}
|
||||
+
|
||||
+static void frbwrite(int l)
|
||||
+{
|
||||
+ /* simple noise filter */
|
||||
+ static int pulse, space;
|
||||
+ static unsigned int ptr;
|
||||
+
|
||||
+ if (ptr > 0 && (l & PULSE_BIT)) {
|
||||
+ pulse += l & PULSE_MASK;
|
||||
+ if (pulse > 250) {
|
||||
+ rbwrite(space);
|
||||
+ rbwrite(pulse | PULSE_BIT);
|
||||
+ ptr = 0;
|
||||
+ pulse = 0;
|
||||
+ }
|
||||
+ return;
|
||||
+ }
|
||||
+ if (!(l & PULSE_BIT)) {
|
||||
+ if (ptr == 0) {
|
||||
+ if (l > 20000) {
|
||||
+ space = l;
|
||||
+ ptr++;
|
||||
+ return;
|
||||
+ }
|
||||
+ } else {
|
||||
+ if (l > 20000) {
|
||||
+ space += pulse;
|
||||
+ if (space > PULSE_MASK)
|
||||
+ space = PULSE_MASK;
|
||||
+ space += l;
|
||||
+ if (space > PULSE_MASK)
|
||||
+ space = PULSE_MASK;
|
||||
+ pulse = 0;
|
||||
+ return;
|
||||
+ }
|
||||
+ rbwrite(space);
|
||||
+ rbwrite(pulse | PULSE_BIT);
|
||||
+ ptr = 0;
|
||||
+ pulse = 0;
|
||||
+ }
|
||||
+ }
|
||||
+ rbwrite(l);
|
||||
+}
|
||||
+
|
||||
+static irqreturn_t irq_handler(int i, void *blah, struct pt_regs *regs)
|
||||
+{
|
||||
+ struct timeval tv;
|
||||
+ long deltv;
|
||||
+ int data;
|
||||
+ int signal;
|
||||
+
|
||||
+ /* use the GPIO signal level */
|
||||
+ signal = gpiochip->get(gpiochip, gpio_in_pin);
|
||||
+
|
||||
+ /* unmask the irq */
|
||||
+ irqchip->irq_unmask(irqdata);
|
||||
+
|
||||
+ if (sense != -1) {
|
||||
+ /* get current time */
|
||||
+ do_gettimeofday(&tv);
|
||||
+
|
||||
+ /* calc time since last interrupt in microseconds */
|
||||
+ deltv = tv.tv_sec-lasttv.tv_sec;
|
||||
+ if (tv.tv_sec < lasttv.tv_sec ||
|
||||
+ (tv.tv_sec == lasttv.tv_sec &&
|
||||
+ tv.tv_usec < lasttv.tv_usec)) {
|
||||
+ printk(KERN_WARNING LIRC_DRIVER_NAME
|
||||
+ ": AIEEEE: your clock just jumped backwards\n");
|
||||
+ printk(KERN_WARNING LIRC_DRIVER_NAME
|
||||
+ ": %d %d %lx %lx %lx %lx\n", signal, sense,
|
||||
+ tv.tv_sec, lasttv.tv_sec,
|
||||
+ tv.tv_usec, lasttv.tv_usec);
|
||||
+ data = PULSE_MASK;
|
||||
+ } else if (deltv > 15) {
|
||||
+ data = PULSE_MASK; /* really long time */
|
||||
+ if (!(signal^sense)) {
|
||||
+ /* sanity check */
|
||||
+ printk(KERN_WARNING LIRC_DRIVER_NAME
|
||||
+ ": AIEEEE: %d %d %lx %lx %lx %lx\n",
|
||||
+ signal, sense, tv.tv_sec, lasttv.tv_sec,
|
||||
+ tv.tv_usec, lasttv.tv_usec);
|
||||
+ /*
|
||||
+ * detecting pulse while this
|
||||
+ * MUST be a space!
|
||||
+ */
|
||||
+ sense = sense ? 0 : 1;
|
||||
+ }
|
||||
+ } else {
|
||||
+ data = (int) (deltv*1000000 +
|
||||
+ (tv.tv_usec - lasttv.tv_usec));
|
||||
+ }
|
||||
+ frbwrite(signal^sense ? data : (data|PULSE_BIT));
|
||||
+ lasttv = tv;
|
||||
+ wake_up_interruptible(&rbuf.wait_poll);
|
||||
+ }
|
||||
+
|
||||
+ return IRQ_HANDLED;
|
||||
+}
|
||||
+
|
||||
+static int is_right_chip(struct gpio_chip *chip, void *data)
|
||||
+{
|
||||
+ dprintk("is_right_chip %s %d\n", chip->label, strcmp(data, chip->label));
|
||||
+
|
||||
+ if (strcmp(data, chip->label) == 0)
|
||||
+ return 1;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int init_port(void)
|
||||
+{
|
||||
+ int i, nlow, nhigh, ret, irq;
|
||||
+
|
||||
+ gpiochip = gpiochip_find("bcm2708_gpio", is_right_chip);
|
||||
+
|
||||
+ if (!gpiochip)
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ if (gpio_request(gpio_out_pin, LIRC_DRIVER_NAME " ir/out")) {
|
||||
+ printk(KERN_ALERT LIRC_DRIVER_NAME
|
||||
+ ": cant claim gpio pin %d\n", gpio_out_pin);
|
||||
+ ret = -ENODEV;
|
||||
+ goto exit_init_port;
|
||||
+ }
|
||||
+
|
||||
+ if (gpio_request(gpio_in_pin, LIRC_DRIVER_NAME " ir/in")) {
|
||||
+ printk(KERN_ALERT LIRC_DRIVER_NAME
|
||||
+ ": cant claim gpio pin %d\n", gpio_in_pin);
|
||||
+ ret = -ENODEV;
|
||||
+ goto exit_gpio_free_out_pin;
|
||||
+ }
|
||||
+
|
||||
+ gpiochip->direction_input(gpiochip, gpio_in_pin);
|
||||
+ gpiochip->direction_output(gpiochip, gpio_out_pin, 1);
|
||||
+ gpiochip->set(gpiochip, gpio_out_pin, invert);
|
||||
+
|
||||
+ irq = gpiochip->to_irq(gpiochip, gpio_in_pin);
|
||||
+ dprintk("to_irq %d\n", irq);
|
||||
+ irqdata = irq_get_irq_data(irq);
|
||||
+
|
||||
+ if (irqdata && irqdata->chip) {
|
||||
+ irqchip = irqdata->chip;
|
||||
+ } else {
|
||||
+ ret = -ENODEV;
|
||||
+ goto exit_gpio_free_in_pin;
|
||||
+ }
|
||||
+
|
||||
+ /* if pin is high, then this must be an active low receiver. */
|
||||
+ if (sense == -1) {
|
||||
+ /* wait 1/2 sec for the power supply */
|
||||
+ msleep(500);
|
||||
+
|
||||
+ /*
|
||||
+ * probe 9 times every 0.04s, collect "votes" for
|
||||
+ * active high/low
|
||||
+ */
|
||||
+ nlow = 0;
|
||||
+ nhigh = 0;
|
||||
+ for (i = 0; i < 9; i++) {
|
||||
+ if (gpiochip->get(gpiochip, gpio_in_pin))
|
||||
+ nlow++;
|
||||
+ else
|
||||
+ nhigh++;
|
||||
+ msleep(40);
|
||||
+ }
|
||||
+ sense = (nlow >= nhigh ? 1 : 0);
|
||||
+ printk(KERN_INFO LIRC_DRIVER_NAME
|
||||
+ ": auto-detected active %s receiver on GPIO pin %d\n",
|
||||
+ sense ? "low" : "high", gpio_in_pin);
|
||||
+ } else {
|
||||
+ printk(KERN_INFO LIRC_DRIVER_NAME
|
||||
+ ": manually using active %s receiver on GPIO pin %d\n",
|
||||
+ sense ? "low" : "high", gpio_in_pin);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+ exit_gpio_free_in_pin:
|
||||
+ gpio_free(gpio_in_pin);
|
||||
+
|
||||
+ exit_gpio_free_out_pin:
|
||||
+ gpio_free(gpio_out_pin);
|
||||
+
|
||||
+ exit_init_port:
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+// called when the character device is opened
|
||||
+static int set_use_inc(void *data)
|
||||
+{
|
||||
+ int result;
|
||||
+ unsigned long flags;
|
||||
+
|
||||
+ /* initialize timestamp */
|
||||
+ do_gettimeofday(&lasttv);
|
||||
+
|
||||
+ result = request_irq(gpiochip->to_irq(gpiochip, gpio_in_pin),
|
||||
+ (irq_handler_t) irq_handler, 0,
|
||||
+ LIRC_DRIVER_NAME, (void*) 0);
|
||||
+
|
||||
+ switch (result) {
|
||||
+ case -EBUSY:
|
||||
+ printk(KERN_ERR LIRC_DRIVER_NAME
|
||||
+ ": IRQ %d is busy\n",
|
||||
+ gpiochip->to_irq(gpiochip, gpio_in_pin));
|
||||
+ return -EBUSY;
|
||||
+ case -EINVAL:
|
||||
+ printk(KERN_ERR LIRC_DRIVER_NAME
|
||||
+ ": Bad irq number or handler\n");
|
||||
+ return -EINVAL;
|
||||
+ default:
|
||||
+ dprintk("Interrupt %d obtained\n",
|
||||
+ gpiochip->to_irq(gpiochip, gpio_in_pin));
|
||||
+ break;
|
||||
+ };
|
||||
+
|
||||
+ /* initialize pulse/space widths */
|
||||
+ init_timing_params(duty_cycle, freq);
|
||||
+
|
||||
+ spin_lock_irqsave(&lock, flags);
|
||||
+
|
||||
+ /* GPIO Pin Falling/Rising Edge Detect Enable */
|
||||
+ irqchip->irq_set_type(irqdata,
|
||||
+ IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING);
|
||||
+
|
||||
+ /* unmask the irq */
|
||||
+ irqchip->irq_unmask(irqdata);
|
||||
+
|
||||
+ spin_unlock_irqrestore(&lock, flags);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void set_use_dec(void *data)
|
||||
+{
|
||||
+ unsigned long flags;
|
||||
+
|
||||
+ spin_lock_irqsave(&lock, flags);
|
||||
+
|
||||
+ /* GPIO Pin Falling/Rising Edge Detect Disable */
|
||||
+ irqchip->irq_set_type(irqdata, 0);
|
||||
+ irqchip->irq_mask(irqdata);
|
||||
+
|
||||
+ spin_unlock_irqrestore(&lock, flags);
|
||||
+
|
||||
+ free_irq(gpiochip->to_irq(gpiochip, gpio_in_pin), (void *) 0);
|
||||
+
|
||||
+ dprintk(KERN_INFO LIRC_DRIVER_NAME
|
||||
+ ": freed IRQ %d\n", gpiochip->to_irq(gpiochip, gpio_in_pin));
|
||||
+}
|
||||
+
|
||||
+static ssize_t lirc_write(struct file *file, const char *buf,
|
||||
+ size_t n, loff_t *ppos)
|
||||
+{
|
||||
+ int i, count;
|
||||
+ unsigned long flags;
|
||||
+ long delta = 0;
|
||||
+ int *wbuf;
|
||||
+
|
||||
+ count = n / sizeof(int);
|
||||
+ if (n % sizeof(int) || count % 2 == 0)
|
||||
+ return -EINVAL;
|
||||
+ wbuf = memdup_user(buf, n);
|
||||
+ if (IS_ERR(wbuf))
|
||||
+ return PTR_ERR(wbuf);
|
||||
+ spin_lock_irqsave(&lock, flags);
|
||||
+
|
||||
+ for (i = 0; i < count; i++) {
|
||||
+ if (i%2)
|
||||
+ send_space(wbuf[i] - delta);
|
||||
+ else
|
||||
+ delta = send_pulse(wbuf[i]);
|
||||
+ }
|
||||
+ gpiochip->set(gpiochip, gpio_out_pin, invert);
|
||||
+
|
||||
+ spin_unlock_irqrestore(&lock, flags);
|
||||
+ kfree(wbuf);
|
||||
+ return n;
|
||||
+}
|
||||
+
|
||||
+static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
|
||||
+{
|
||||
+ int result;
|
||||
+ __u32 value;
|
||||
+
|
||||
+ switch (cmd) {
|
||||
+ case LIRC_GET_SEND_MODE:
|
||||
+ return -ENOIOCTLCMD;
|
||||
+ break;
|
||||
+
|
||||
+ case LIRC_SET_SEND_MODE:
|
||||
+ result = get_user(value, (__u32 *) arg);
|
||||
+ if (result)
|
||||
+ return result;
|
||||
+ /* only LIRC_MODE_PULSE supported */
|
||||
+ if (value != LIRC_MODE_PULSE)
|
||||
+ return -ENOSYS;
|
||||
+ break;
|
||||
+
|
||||
+ case LIRC_GET_LENGTH:
|
||||
+ return -ENOSYS;
|
||||
+ break;
|
||||
+
|
||||
+ case LIRC_SET_SEND_DUTY_CYCLE:
|
||||
+ dprintk("SET_SEND_DUTY_CYCLE\n");
|
||||
+ result = get_user(value, (__u32 *) arg);
|
||||
+ if (result)
|
||||
+ return result;
|
||||
+ if (value <= 0 || value > 100)
|
||||
+ return -EINVAL;
|
||||
+ return init_timing_params(value, freq);
|
||||
+ break;
|
||||
+
|
||||
+ case LIRC_SET_SEND_CARRIER:
|
||||
+ dprintk("SET_SEND_CARRIER\n");
|
||||
+ result = get_user(value, (__u32 *) arg);
|
||||
+ if (result)
|
||||
+ return result;
|
||||
+ if (value > 500000 || value < 20000)
|
||||
+ return -EINVAL;
|
||||
+ return init_timing_params(duty_cycle, value);
|
||||
+ break;
|
||||
+
|
||||
+ default:
|
||||
+ return lirc_dev_fop_ioctl(filep, cmd, arg);
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct file_operations lirc_fops = {
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .write = lirc_write,
|
||||
+ .unlocked_ioctl = lirc_ioctl,
|
||||
+ .read = lirc_dev_fop_read,
|
||||
+ .poll = lirc_dev_fop_poll,
|
||||
+ .open = lirc_dev_fop_open,
|
||||
+ .release = lirc_dev_fop_close,
|
||||
+ .llseek = no_llseek,
|
||||
+};
|
||||
+
|
||||
+static struct lirc_driver driver = {
|
||||
+ .name = LIRC_DRIVER_NAME,
|
||||
+ .minor = -1,
|
||||
+ .code_length = 1,
|
||||
+ .sample_rate = 0,
|
||||
+ .data = NULL,
|
||||
+ .add_to_buf = NULL,
|
||||
+ .rbuf = &rbuf,
|
||||
+ .set_use_inc = set_use_inc,
|
||||
+ .set_use_dec = set_use_dec,
|
||||
+ .fops = &lirc_fops,
|
||||
+ .dev = NULL,
|
||||
+ .owner = THIS_MODULE,
|
||||
+};
|
||||
+
|
||||
+static struct platform_driver lirc_rpi_driver = {
|
||||
+ .driver = {
|
||||
+ .name = LIRC_DRIVER_NAME,
|
||||
+ .owner = THIS_MODULE,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+static int __init lirc_rpi_init(void)
|
||||
+{
|
||||
+ int result;
|
||||
+
|
||||
+ /* Init read buffer. */
|
||||
+ result = lirc_buffer_init(&rbuf, sizeof(int), RBUF_LEN);
|
||||
+ if (result < 0)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ result = platform_driver_register(&lirc_rpi_driver);
|
||||
+ if (result) {
|
||||
+ printk(KERN_ERR LIRC_DRIVER_NAME
|
||||
+ ": lirc register returned %d\n", result);
|
||||
+ goto exit_buffer_free;
|
||||
+ }
|
||||
+
|
||||
+ lirc_rpi_dev = platform_device_alloc(LIRC_DRIVER_NAME, 0);
|
||||
+ if (!lirc_rpi_dev) {
|
||||
+ result = -ENOMEM;
|
||||
+ goto exit_driver_unregister;
|
||||
+ }
|
||||
+
|
||||
+ result = platform_device_add(lirc_rpi_dev);
|
||||
+ if (result)
|
||||
+ goto exit_device_put;
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+ exit_device_put:
|
||||
+ platform_device_put(lirc_rpi_dev);
|
||||
+
|
||||
+ exit_driver_unregister:
|
||||
+ platform_driver_unregister(&lirc_rpi_driver);
|
||||
+
|
||||
+ exit_buffer_free:
|
||||
+ lirc_buffer_free(&rbuf);
|
||||
+
|
||||
+ return result;
|
||||
+}
|
||||
+
|
||||
+static void lirc_rpi_exit(void)
|
||||
+{
|
||||
+ platform_device_unregister(lirc_rpi_dev);
|
||||
+ platform_driver_unregister(&lirc_rpi_driver);
|
||||
+ lirc_buffer_free(&rbuf);
|
||||
+}
|
||||
+
|
||||
+static int __init lirc_rpi_init_module(void)
|
||||
+{
|
||||
+ int result, i;
|
||||
+
|
||||
+ result = lirc_rpi_init();
|
||||
+ if (result)
|
||||
+ return result;
|
||||
+
|
||||
+ /* check if the module received valid gpio pin numbers */
|
||||
+ result = 0;
|
||||
+ if (gpio_in_pin != gpio_out_pin) {
|
||||
+ for(i = 0; (i < ARRAY_SIZE(valid_gpio_pins)) && (result != 2); i++) {
|
||||
+ if (gpio_in_pin == valid_gpio_pins[i] ||
|
||||
+ gpio_out_pin == valid_gpio_pins[i]) {
|
||||
+ result++;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (result != 2) {
|
||||
+ result = -EINVAL;
|
||||
+ printk(KERN_ERR LIRC_DRIVER_NAME
|
||||
+ ": invalid GPIO pin(s) specified!\n");
|
||||
+ goto exit_rpi;
|
||||
+ }
|
||||
+
|
||||
+ result = init_port();
|
||||
+ if (result < 0)
|
||||
+ goto exit_rpi;
|
||||
+
|
||||
+ driver.features = LIRC_CAN_SET_SEND_DUTY_CYCLE |
|
||||
+ LIRC_CAN_SET_SEND_CARRIER |
|
||||
+ LIRC_CAN_SEND_PULSE |
|
||||
+ LIRC_CAN_REC_MODE2;
|
||||
+
|
||||
+ driver.dev = &lirc_rpi_dev->dev;
|
||||
+ driver.minor = lirc_register_driver(&driver);
|
||||
+
|
||||
+ if (driver.minor < 0) {
|
||||
+ printk(KERN_ERR LIRC_DRIVER_NAME
|
||||
+ ": device registration failed with %d\n", result);
|
||||
+ result = -EIO;
|
||||
+ goto exit_rpi;
|
||||
+ }
|
||||
+
|
||||
+ printk(KERN_INFO LIRC_DRIVER_NAME ": driver registered!\n");
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+ exit_rpi:
|
||||
+ lirc_rpi_exit();
|
||||
+
|
||||
+ return result;
|
||||
+}
|
||||
+
|
||||
+static void __exit lirc_rpi_exit_module(void)
|
||||
+{
|
||||
+ gpio_free(gpio_out_pin);
|
||||
+ gpio_free(gpio_in_pin);
|
||||
+
|
||||
+ lirc_rpi_exit();
|
||||
+
|
||||
+ lirc_unregister_driver(driver.minor);
|
||||
+ printk(KERN_INFO LIRC_DRIVER_NAME ": cleaned up module\n");
|
||||
+}
|
||||
+
|
||||
+module_init(lirc_rpi_init_module);
|
||||
+module_exit(lirc_rpi_exit_module);
|
||||
+
|
||||
+MODULE_DESCRIPTION("Infra-red receiver and blaster driver for Raspberry Pi GPIO.");
|
||||
+MODULE_AUTHOR("Aron Robert Szabo <aron@reon.hu>");
|
||||
+MODULE_AUTHOR("Michael Bishop <cleverca22@gmail.com>");
|
||||
+MODULE_LICENSE("GPL");
|
||||
+
|
||||
+module_param(gpio_out_pin, int, S_IRUGO);
|
||||
+MODULE_PARM_DESC(gpio_out_pin, "GPIO output/transmitter pin number of the BCM"
|
||||
+ " processor. Valid pin numbers are: 0, 1, 4, 8, 7, 9, 10, 11,"
|
||||
+ " 14, 15, 17, 18, 21, 22, 23, 24, 25, default 17");
|
||||
+
|
||||
+module_param(gpio_in_pin, int, S_IRUGO);
|
||||
+MODULE_PARM_DESC(gpio_in_pin, "GPIO input pin number of the BCM processor."
|
||||
+ " Valid pin numbers are: 0, 1, 4, 8, 7, 9, 10, 11, 14, 15,"
|
||||
+ " 17, 18, 21, 22, 23, 24, 25, default 18");
|
||||
+
|
||||
+module_param(sense, int, S_IRUGO);
|
||||
+MODULE_PARM_DESC(sense, "Override autodetection of IR receiver circuit"
|
||||
+ " (0 = active high, 1 = active low )");
|
||||
+
|
||||
+module_param(softcarrier, bool, S_IRUGO);
|
||||
+MODULE_PARM_DESC(softcarrier, "Software carrier (0 = off, 1 = on, default on)");
|
||||
+
|
||||
+module_param(invert, bool, S_IRUGO);
|
||||
+MODULE_PARM_DESC(invert, "Invert output (0 = off, 1 = on, default off");
|
||||
+
|
||||
+module_param(debug, bool, S_IRUGO | S_IWUSR);
|
||||
+MODULE_PARM_DESC(debug, "Enable debugging messages");
|
File diff suppressed because it is too large
Load diff
|
@ -1,292 +0,0 @@
|
|||
From 4882beaf73302c162f08620d770f020211bf0acb Mon Sep 17 00:00:00 2001
|
||||
From: popcornmix <popcornmix@gmail.com>
|
||||
Date: Wed, 3 Jul 2013 00:49:20 +0100
|
||||
Subject: [PATCH 14/54] Add cpufreq driver
|
||||
|
||||
---
|
||||
arch/arm/Kconfig | 1 +
|
||||
drivers/cpufreq/Kconfig.arm | 8 ++
|
||||
drivers/cpufreq/Makefile | 1 +
|
||||
drivers/cpufreq/bcm2835-cpufreq.c | 239 ++++++++++++++++++++++++++++++++++++++
|
||||
4 files changed, 249 insertions(+)
|
||||
create mode 100755 drivers/cpufreq/bcm2835-cpufreq.c
|
||||
|
||||
--- a/arch/arm/Kconfig
|
||||
+++ b/arch/arm/Kconfig
|
||||
@@ -389,6 +389,7 @@ config ARCH_BCM2708
|
||||
select NEED_MACH_GPIO_H
|
||||
select NEED_MACH_MEMORY_H
|
||||
select CLKDEV_LOOKUP
|
||||
+ select ARCH_HAS_CPUFREQ
|
||||
select GENERIC_CLOCKEVENTS
|
||||
select ARM_ERRATA_411920
|
||||
select MACH_BCM2708
|
||||
--- a/drivers/cpufreq/Kconfig.arm
|
||||
+++ b/drivers/cpufreq/Kconfig.arm
|
||||
@@ -235,6 +235,14 @@ config ARM_SPEAR_CPUFREQ
|
||||
help
|
||||
This adds the CPUFreq driver support for SPEAr SOCs.
|
||||
|
||||
+config ARM_BCM2835_CPUFREQ
|
||||
+ bool "BCM2835 Driver"
|
||||
+ default y
|
||||
+ help
|
||||
+ This adds the CPUFreq driver for BCM2835
|
||||
+
|
||||
+ If in doubt, say N.
|
||||
+
|
||||
config ARM_TEGRA_CPUFREQ
|
||||
bool "TEGRA CPUFreq support"
|
||||
depends on ARCH_TEGRA
|
||||
--- a/drivers/cpufreq/Makefile
|
||||
+++ b/drivers/cpufreq/Makefile
|
||||
@@ -73,6 +73,7 @@ obj-$(CONFIG_ARM_S5PV210_CPUFREQ) += s5p
|
||||
obj-$(CONFIG_ARM_SA1100_CPUFREQ) += sa1100-cpufreq.o
|
||||
obj-$(CONFIG_ARM_SA1110_CPUFREQ) += sa1110-cpufreq.o
|
||||
obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o
|
||||
+obj-$(CONFIG_ARM_BCM2835_CPUFREQ) += bcm2835-cpufreq.o
|
||||
obj-$(CONFIG_ARM_TEGRA_CPUFREQ) += tegra-cpufreq.o
|
||||
obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o
|
||||
|
||||
--- /dev/null
|
||||
+++ b/drivers/cpufreq/bcm2835-cpufreq.c
|
||||
@@ -0,0 +1,239 @@
|
||||
+/*****************************************************************************
|
||||
+* Copyright 2011 Broadcom Corporation. All rights reserved.
|
||||
+*
|
||||
+* Unless you and Broadcom execute a separate written software license
|
||||
+* agreement governing use of this software, this software is licensed to you
|
||||
+* under the terms of the GNU General Public License version 2, available at
|
||||
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
|
||||
+*
|
||||
+* Notwithstanding the above, under no circumstances may you combine this
|
||||
+* software in any way with any other Broadcom software provided under a
|
||||
+* license other than the GPL, without Broadcom's express prior written
|
||||
+* consent.
|
||||
+*****************************************************************************/
|
||||
+
|
||||
+/*****************************************************************************
|
||||
+* FILENAME: bcm2835-cpufreq.h
|
||||
+* DESCRIPTION: This driver dynamically manages the CPU Frequency of the ARM
|
||||
+* processor. Messages are sent to Videocore either setting or requesting the
|
||||
+* frequency of the ARM in order to match an appropiate frequency to the current
|
||||
+* usage of the processor. The policy which selects the frequency to use is
|
||||
+* defined in the kernel .config file, but can be changed during runtime.
|
||||
+*****************************************************************************/
|
||||
+
|
||||
+/* ---------- INCLUDES ---------- */
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/cpufreq.h>
|
||||
+#include <mach/vcio.h>
|
||||
+
|
||||
+/* ---------- DEFINES ---------- */
|
||||
+/*#define CPUFREQ_DEBUG_ENABLE*/ /* enable debugging */
|
||||
+#define MODULE_NAME "bcm2835-cpufreq"
|
||||
+
|
||||
+#define VCMSG_ID_ARM_CLOCK 0x000000003 /* Clock/Voltage ID's */
|
||||
+
|
||||
+/* debug printk macros */
|
||||
+#ifdef CPUFREQ_DEBUG_ENABLE
|
||||
+#define print_debug(fmt,...) pr_debug("%s:%s:%d: "fmt, MODULE_NAME, __func__, __LINE__, ##__VA_ARGS__)
|
||||
+#else
|
||||
+#define print_debug(fmt,...)
|
||||
+#endif
|
||||
+#define print_err(fmt,...) pr_err("%s:%s:%d: "fmt, MODULE_NAME, __func__,__LINE__, ##__VA_ARGS__)
|
||||
+#define print_info(fmt,...) pr_info("%s: "fmt, MODULE_NAME, ##__VA_ARGS__)
|
||||
+
|
||||
+/* tag part of the message */
|
||||
+struct vc_msg_tag {
|
||||
+ uint32_t tag_id; /* the message id */
|
||||
+ uint32_t buffer_size; /* size of the buffer (which in this case is always 8 bytes) */
|
||||
+ uint32_t data_size; /* amount of data being sent or received */
|
||||
+ uint32_t dev_id; /* the ID of the clock/voltage to get or set */
|
||||
+ uint32_t val; /* the value (e.g. rate (in Hz)) to set */
|
||||
+};
|
||||
+
|
||||
+/* message structure to be sent to videocore */
|
||||
+struct vc_msg {
|
||||
+ uint32_t msg_size; /* simply, sizeof(struct vc_msg) */
|
||||
+ uint32_t request_code; /* holds various information like the success and number of bytes returned (refer to mailboxes wiki) */
|
||||
+ struct vc_msg_tag tag; /* the tag structure above to make */
|
||||
+ uint32_t end_tag; /* an end identifier, should be set to NULL */
|
||||
+};
|
||||
+
|
||||
+/* ---------- GLOBALS ---------- */
|
||||
+static struct cpufreq_driver bcm2835_cpufreq_driver; /* the cpufreq driver global */
|
||||
+
|
||||
+/*
|
||||
+ ===============================================
|
||||
+ clk_rate either gets or sets the clock rates.
|
||||
+ ===============================================
|
||||
+*/
|
||||
+static uint32_t bcm2835_cpufreq_set_clock(int cur_rate, int arm_rate)
|
||||
+{
|
||||
+ int s, actual_rate=0;
|
||||
+ struct vc_msg msg;
|
||||
+
|
||||
+ /* wipe all previous message data */
|
||||
+ memset(&msg, 0, sizeof msg);
|
||||
+
|
||||
+ msg.msg_size = sizeof msg;
|
||||
+
|
||||
+ msg.tag.tag_id = VCMSG_SET_CLOCK_RATE;
|
||||
+ msg.tag.buffer_size = 8;
|
||||
+ msg.tag.data_size = 8; /* we're sending the clock ID and the new rates which is a total of 2 words */
|
||||
+ msg.tag.dev_id = VCMSG_ID_ARM_CLOCK;
|
||||
+ msg.tag.val = arm_rate * 1000;
|
||||
+
|
||||
+ /* send the message */
|
||||
+ s = bcm_mailbox_property(&msg, sizeof msg);
|
||||
+
|
||||
+ /* check if it was all ok and return the rate in KHz */
|
||||
+ if (s == 0 && (msg.request_code & 0x80000000))
|
||||
+ actual_rate = msg.tag.val/1000;
|
||||
+
|
||||
+ print_debug("Setting new frequency = %d -> %d (actual %d)\n", cur_rate, arm_rate, actual_rate);
|
||||
+ return actual_rate;
|
||||
+}
|
||||
+
|
||||
+static uint32_t bcm2835_cpufreq_get_clock(int tag)
|
||||
+{
|
||||
+ int s;
|
||||
+ int arm_rate = 0;
|
||||
+ struct vc_msg msg;
|
||||
+
|
||||
+ /* wipe all previous message data */
|
||||
+ memset(&msg, 0, sizeof msg);
|
||||
+
|
||||
+ msg.msg_size = sizeof msg;
|
||||
+ msg.tag.tag_id = tag;
|
||||
+ msg.tag.buffer_size = 8;
|
||||
+ msg.tag.data_size = 4; /* we're just sending the clock ID which is one word long */
|
||||
+ msg.tag.dev_id = VCMSG_ID_ARM_CLOCK;
|
||||
+
|
||||
+ /* send the message */
|
||||
+ s = bcm_mailbox_property(&msg, sizeof msg);
|
||||
+
|
||||
+ /* check if it was all ok and return the rate in KHz */
|
||||
+ if (s == 0 && (msg.request_code & 0x80000000))
|
||||
+ arm_rate = msg.tag.val/1000;
|
||||
+
|
||||
+ print_debug("%s frequency = %d\n",
|
||||
+ tag == VCMSG_GET_CLOCK_RATE ? "Current":
|
||||
+ tag == VCMSG_GET_MIN_CLOCK ? "Min":
|
||||
+ tag == VCMSG_GET_MAX_CLOCK ? "Max":
|
||||
+ "Unexpected", arm_rate);
|
||||
+
|
||||
+ return arm_rate;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ ====================================================
|
||||
+ Module Initialisation registers the cpufreq driver
|
||||
+ ====================================================
|
||||
+*/
|
||||
+static int __init bcm2835_cpufreq_module_init(void)
|
||||
+{
|
||||
+ print_debug("IN\n");
|
||||
+ return cpufreq_register_driver(&bcm2835_cpufreq_driver);
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ =============
|
||||
+ Module exit
|
||||
+ =============
|
||||
+*/
|
||||
+static void __exit bcm2835_cpufreq_module_exit(void)
|
||||
+{
|
||||
+ print_debug("IN\n");
|
||||
+ cpufreq_unregister_driver(&bcm2835_cpufreq_driver);
|
||||
+ return;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ ==============================================================
|
||||
+ Initialisation function sets up the CPU policy for first use
|
||||
+ ==============================================================
|
||||
+*/
|
||||
+static int bcm2835_cpufreq_driver_init(struct cpufreq_policy *policy)
|
||||
+{
|
||||
+ /* measured value of how long it takes to change frequency */
|
||||
+ policy->cpuinfo.transition_latency = 355000; /* ns */
|
||||
+
|
||||
+ /* now find out what the maximum and minimum frequencies are */
|
||||
+ policy->min = policy->cpuinfo.min_freq = bcm2835_cpufreq_get_clock(VCMSG_GET_MIN_CLOCK);
|
||||
+ policy->max = policy->cpuinfo.max_freq = bcm2835_cpufreq_get_clock(VCMSG_GET_MAX_CLOCK);
|
||||
+ policy->cur = bcm2835_cpufreq_get_clock(VCMSG_GET_CLOCK_RATE);
|
||||
+
|
||||
+ print_info("min=%d max=%d cur=%d\n", policy->min, policy->max, policy->cur);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ =================================================================================
|
||||
+ Target function chooses the most appropriate frequency from the table to enable
|
||||
+ =================================================================================
|
||||
+*/
|
||||
+
|
||||
+static int bcm2835_cpufreq_driver_target(struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation)
|
||||
+{
|
||||
+ unsigned int target = target_freq;
|
||||
+#ifdef CPUFREQ_DEBUG_ENABLE
|
||||
+ unsigned int cur = policy->cur;
|
||||
+#endif
|
||||
+ print_debug("%s: min=%d max=%d cur=%d target=%d\n",policy->governor->name,policy->min,policy->max,policy->cur,target_freq);
|
||||
+
|
||||
+ /* if we are above min and using ondemand, then just use max */
|
||||
+ if (strcmp("ondemand", policy->governor->name)==0 && target > policy->min)
|
||||
+ target = policy->max;
|
||||
+ /* if the frequency is the same, just quit */
|
||||
+ if (target == policy->cur)
|
||||
+ return 0;
|
||||
+
|
||||
+ /* otherwise were good to set the clock frequency */
|
||||
+ policy->cur = bcm2835_cpufreq_set_clock(policy->cur, target);
|
||||
+
|
||||
+ if (!policy->cur)
|
||||
+ {
|
||||
+ print_err("Error occurred setting a new frequency (%d)!\n", target);
|
||||
+ policy->cur = bcm2835_cpufreq_get_clock(VCMSG_GET_CLOCK_RATE);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+ print_debug("Freq %d->%d (min=%d max=%d target=%d request=%d)\n", cur, policy->cur, policy->min, policy->max, target_freq, target);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static unsigned int bcm2835_cpufreq_driver_get(unsigned int cpu)
|
||||
+{
|
||||
+ unsigned int actual_rate = bcm2835_cpufreq_get_clock(VCMSG_GET_CLOCK_RATE);
|
||||
+ print_debug("cpu=%d\n", actual_rate);
|
||||
+ return actual_rate;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ =================================================================================
|
||||
+ Verify ensures that when a policy is changed, it is suitable for the CPU to use
|
||||
+ =================================================================================
|
||||
+*/
|
||||
+
|
||||
+static int bcm2835_cpufreq_driver_verify(struct cpufreq_policy *policy)
|
||||
+{
|
||||
+ print_info("switching to governor %s\n", policy->governor->name);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+/* the CPUFreq driver */
|
||||
+static struct cpufreq_driver bcm2835_cpufreq_driver = {
|
||||
+ .name = "BCM2835 CPUFreq",
|
||||
+ .init = bcm2835_cpufreq_driver_init,
|
||||
+ .verify = bcm2835_cpufreq_driver_verify,
|
||||
+ .target = bcm2835_cpufreq_driver_target,
|
||||
+ .get = bcm2835_cpufreq_driver_get
|
||||
+};
|
||||
+
|
||||
+MODULE_AUTHOR("Dorian Peake and Dom Cobley");
|
||||
+MODULE_DESCRIPTION("CPU frequency driver for BCM2835 chip");
|
||||
+MODULE_LICENSE("GPL");
|
||||
+
|
||||
+module_init(bcm2835_cpufreq_module_init);
|
||||
+module_exit(bcm2835_cpufreq_module_exit);
|
|
@ -1,498 +0,0 @@
|
|||
From 22964c3b89f28e0957965aee59e713670d3a7729 Mon Sep 17 00:00:00 2001
|
||||
From: popcornmix <popcornmix@gmail.com>
|
||||
Date: Tue, 26 Mar 2013 19:24:24 +0000
|
||||
Subject: [PATCH 15/54] Added hwmon/thermal driver for reporting core
|
||||
temperature. Thanks Dorian
|
||||
|
||||
---
|
||||
arch/arm/mach-bcm2708/bcm2708.c | 11 ++
|
||||
drivers/hwmon/Kconfig | 10 ++
|
||||
drivers/hwmon/Makefile | 1 +
|
||||
drivers/hwmon/bcm2835-hwmon.c | 219 ++++++++++++++++++++++++++++++++++++++
|
||||
drivers/thermal/Kconfig | 6 ++
|
||||
drivers/thermal/Makefile | 1 +
|
||||
drivers/thermal/bcm2835-thermal.c | 184 ++++++++++++++++++++++++++++++++
|
||||
7 files changed, 432 insertions(+)
|
||||
create mode 100644 drivers/hwmon/bcm2835-hwmon.c
|
||||
create mode 100644 drivers/thermal/bcm2835-thermal.c
|
||||
|
||||
--- a/arch/arm/mach-bcm2708/bcm2708.c
|
||||
+++ b/arch/arm/mach-bcm2708/bcm2708.c
|
||||
@@ -483,6 +483,14 @@ static struct platform_device bcm2708_al
|
||||
},
|
||||
};
|
||||
|
||||
+static struct platform_device bcm2835_hwmon_device = {
|
||||
+ .name = "bcm2835_hwmon",
|
||||
+};
|
||||
+
|
||||
+static struct platform_device bcm2835_thermal_device = {
|
||||
+ .name = "bcm2835_thermal",
|
||||
+};
|
||||
+
|
||||
int __init bcm_register_device(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
@@ -594,6 +602,9 @@ void __init bcm2708_init(void)
|
||||
for (i = 0; i < ARRAY_SIZE(bcm2708_alsa_devices); i++)
|
||||
bcm_register_device(&bcm2708_alsa_devices[i]);
|
||||
|
||||
+ bcm_register_device(&bcm2835_hwmon_device);
|
||||
+ bcm_register_device(&bcm2835_thermal_device);
|
||||
+
|
||||
for (i = 0; i < ARRAY_SIZE(amba_devs); i++) {
|
||||
struct amba_device *d = amba_devs[i];
|
||||
amba_device_register(d, &iomem_resource);
|
||||
--- a/drivers/hwmon/Kconfig
|
||||
+++ b/drivers/hwmon/Kconfig
|
||||
@@ -1565,6 +1565,16 @@ config SENSORS_MC13783_ADC
|
||||
help
|
||||
Support for the A/D converter on MC13783 and MC13892 PMIC.
|
||||
|
||||
+config SENSORS_BCM2835
|
||||
+ depends on THERMAL_BCM2835=n
|
||||
+ tristate "Broadcom BCM2835 HWMON Driver"
|
||||
+ help
|
||||
+ If you say yes here you get support for the hardware
|
||||
+ monitoring features of the BCM2835 Chip
|
||||
+
|
||||
+ This driver can also be built as a module. If so, the module
|
||||
+ will be called bcm2835-hwmon.
|
||||
+
|
||||
if ACPI
|
||||
|
||||
comment "ACPI drivers"
|
||||
--- /dev/null
|
||||
+++ b/drivers/hwmon/bcm2835-hwmon.c
|
||||
@@ -0,0 +1,219 @@
|
||||
+/*****************************************************************************
|
||||
+* Copyright 2011 Broadcom Corporation. All rights reserved.
|
||||
+*
|
||||
+* Unless you and Broadcom execute a separate written software license
|
||||
+* agreement governing use of this software, this software is licensed to you
|
||||
+* under the terms of the GNU General Public License version 2, available at
|
||||
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
|
||||
+*
|
||||
+* Notwithstanding the above, under no circumstances may you combine this
|
||||
+* software in any way with any other Broadcom software provided under a
|
||||
+* license other than the GPL, without Broadcom's express prior written
|
||||
+* consent.
|
||||
+*****************************************************************************/
|
||||
+
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/hwmon.h>
|
||||
+#include <linux/hwmon-sysfs.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/sysfs.h>
|
||||
+#include <mach/vcio.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/err.h>
|
||||
+
|
||||
+#define MODULE_NAME "bcm2835_hwmon"
|
||||
+
|
||||
+/*#define HWMON_DEBUG_ENABLE*/
|
||||
+
|
||||
+#ifdef HWMON_DEBUG_ENABLE
|
||||
+#define print_debug(fmt,...) printk(KERN_INFO "%s:%s:%d: "fmt"\n", MODULE_NAME, __func__, __LINE__, ##__VA_ARGS__)
|
||||
+#else
|
||||
+#define print_debug(fmt,...)
|
||||
+#endif
|
||||
+#define print_err(fmt,...) printk(KERN_ERR "%s:%s:%d: "fmt"\n", MODULE_NAME, __func__,__LINE__, ##__VA_ARGS__)
|
||||
+#define print_info(fmt,...) printk(KERN_INFO "%s: "fmt"\n", MODULE_NAME, ##__VA_ARGS__)
|
||||
+
|
||||
+#define VC_TAG_GET_TEMP 0x00030006
|
||||
+#define VC_TAG_GET_MAX_TEMP 0x0003000A
|
||||
+
|
||||
+/* --- STRUCTS --- */
|
||||
+struct bcm2835_hwmon_data {
|
||||
+ struct device *hwmon_dev;
|
||||
+};
|
||||
+
|
||||
+/* tag part of the message */
|
||||
+struct vc_msg_tag {
|
||||
+ uint32_t tag_id; /* the tag ID for the temperature */
|
||||
+ uint32_t buffer_size; /* size of the buffer (should be 8) */
|
||||
+ uint32_t request_code; /* identifies message as a request (should be 0) */
|
||||
+ uint32_t id; /* extra ID field (should be 0) */
|
||||
+ uint32_t val; /* returned value of the temperature */
|
||||
+};
|
||||
+
|
||||
+/* message structure to be sent to videocore */
|
||||
+struct vc_msg {
|
||||
+ uint32_t msg_size; /* simply, sizeof(struct vc_msg) */
|
||||
+ uint32_t request_code; /* holds various information like the success and number of bytes returned (refer to mailboxes wiki) */
|
||||
+ struct vc_msg_tag tag; /* the tag structure above to make */
|
||||
+ uint32_t end_tag; /* an end identifier, should be set to NULL */
|
||||
+};
|
||||
+
|
||||
+typedef enum {
|
||||
+ TEMP,
|
||||
+ MAX_TEMP,
|
||||
+} temp_type;
|
||||
+
|
||||
+/* --- PROTOTYPES --- */
|
||||
+static ssize_t bcm2835_get_temp(struct device *dev, struct device_attribute *attr, char *buf);
|
||||
+static ssize_t bcm2835_get_name(struct device *dev, struct device_attribute *attr, char *buf);
|
||||
+
|
||||
+/* --- GLOBALS --- */
|
||||
+
|
||||
+static struct bcm2835_hwmon_data *bcm2835_data;
|
||||
+static struct platform_driver bcm2835_hwmon_driver;
|
||||
+
|
||||
+static SENSOR_DEVICE_ATTR(name, S_IRUGO,bcm2835_get_name,NULL,0);
|
||||
+static SENSOR_DEVICE_ATTR(temp1_input,S_IRUGO,bcm2835_get_temp,NULL,TEMP);
|
||||
+static SENSOR_DEVICE_ATTR(temp1_max,S_IRUGO,bcm2835_get_temp,NULL,MAX_TEMP);
|
||||
+
|
||||
+static struct attribute* bcm2835_attributes[] = {
|
||||
+ &sensor_dev_attr_name.dev_attr.attr,
|
||||
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
|
||||
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
|
||||
+ NULL,
|
||||
+};
|
||||
+
|
||||
+static struct attribute_group bcm2835_attr_group = {
|
||||
+ .attrs = bcm2835_attributes,
|
||||
+};
|
||||
+
|
||||
+/* --- FUNCTIONS --- */
|
||||
+
|
||||
+static ssize_t bcm2835_get_name(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
+{
|
||||
+ return sprintf(buf,"bcm2835_hwmon\n");
|
||||
+}
|
||||
+
|
||||
+static ssize_t bcm2835_get_temp(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
+{
|
||||
+ struct vc_msg msg;
|
||||
+ int result;
|
||||
+ uint temp = 0;
|
||||
+ int index = ((struct sensor_device_attribute*)to_sensor_dev_attr(attr))->index;
|
||||
+
|
||||
+ print_debug("IN");
|
||||
+
|
||||
+ /* wipe all previous message data */
|
||||
+ memset(&msg, 0, sizeof msg);
|
||||
+
|
||||
+ /* determine the message type */
|
||||
+ if(index == TEMP)
|
||||
+ msg.tag.tag_id = VC_TAG_GET_TEMP;
|
||||
+ else if (index == MAX_TEMP)
|
||||
+ msg.tag.tag_id = VC_TAG_GET_MAX_TEMP;
|
||||
+ else
|
||||
+ {
|
||||
+ print_debug("Unknown temperature message!");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ msg.msg_size = sizeof msg;
|
||||
+ msg.tag.buffer_size = 8;
|
||||
+
|
||||
+ /* send the message */
|
||||
+ result = bcm_mailbox_property(&msg, sizeof msg);
|
||||
+
|
||||
+ /* check if it was all ok and return the rate in milli degrees C */
|
||||
+ if (result == 0 && (msg.request_code & 0x80000000))
|
||||
+ temp = (uint)msg.tag.val;
|
||||
+ #ifdef HWMON_DEBUG_ENABLE
|
||||
+ else
|
||||
+ print_debug("Failed to get temperature!");
|
||||
+ #endif
|
||||
+ print_debug("Got temperature as %u",temp);
|
||||
+ print_debug("OUT");
|
||||
+ return sprintf(buf, "%u\n", temp);
|
||||
+}
|
||||
+
|
||||
+
|
||||
+static int bcm2835_hwmon_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ int err;
|
||||
+
|
||||
+ print_debug("IN");
|
||||
+ print_debug("HWMON Driver has been probed!");
|
||||
+
|
||||
+ /* check that the device isn't null!*/
|
||||
+ if(pdev == NULL)
|
||||
+ {
|
||||
+ print_debug("Platform device is empty!");
|
||||
+ return -ENODEV;
|
||||
+ }
|
||||
+
|
||||
+ /* allocate memory for neccessary data */
|
||||
+ bcm2835_data = kzalloc(sizeof(struct bcm2835_hwmon_data),GFP_KERNEL);
|
||||
+ if(!bcm2835_data)
|
||||
+ {
|
||||
+ print_debug("Unable to allocate memory for hwmon data!");
|
||||
+ err = -ENOMEM;
|
||||
+ goto kzalloc_error;
|
||||
+ }
|
||||
+
|
||||
+ /* create the sysfs files */
|
||||
+ if(sysfs_create_group(&pdev->dev.kobj, &bcm2835_attr_group))
|
||||
+ {
|
||||
+ print_debug("Unable to create sysfs files!");
|
||||
+ err = -EFAULT;
|
||||
+ goto sysfs_error;
|
||||
+ }
|
||||
+
|
||||
+ /* register the hwmon device */
|
||||
+ bcm2835_data->hwmon_dev = hwmon_device_register(&pdev->dev);
|
||||
+ if (IS_ERR(bcm2835_data->hwmon_dev))
|
||||
+ {
|
||||
+ err = PTR_ERR(bcm2835_data->hwmon_dev);
|
||||
+ goto hwmon_error;
|
||||
+ }
|
||||
+ print_debug("OUT");
|
||||
+ return 0;
|
||||
+
|
||||
+ /* error goto's */
|
||||
+ hwmon_error:
|
||||
+ sysfs_remove_group(&pdev->dev.kobj, &bcm2835_attr_group);
|
||||
+
|
||||
+ sysfs_error:
|
||||
+ kfree(bcm2835_data);
|
||||
+
|
||||
+ kzalloc_error:
|
||||
+
|
||||
+ return err;
|
||||
+
|
||||
+}
|
||||
+
|
||||
+static int bcm2835_hwmon_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ print_debug("IN");
|
||||
+ hwmon_device_unregister(bcm2835_data->hwmon_dev);
|
||||
+
|
||||
+ sysfs_remove_group(&pdev->dev.kobj, &bcm2835_attr_group);
|
||||
+ print_debug("OUT");
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/* Hwmon Driver */
|
||||
+static struct platform_driver bcm2835_hwmon_driver = {
|
||||
+ .probe = bcm2835_hwmon_probe,
|
||||
+ .remove = bcm2835_hwmon_remove,
|
||||
+ .driver = {
|
||||
+ .name = "bcm2835_hwmon",
|
||||
+ .owner = THIS_MODULE,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+MODULE_LICENSE("GPL");
|
||||
+MODULE_AUTHOR("Dorian Peake");
|
||||
+MODULE_DESCRIPTION("HW Monitor driver for bcm2835 chip");
|
||||
+
|
||||
+module_platform_driver(bcm2835_hwmon_driver);
|
||||
--- a/drivers/thermal/Kconfig
|
||||
+++ b/drivers/thermal/Kconfig
|
||||
@@ -196,6 +196,12 @@ config INTEL_POWERCLAMP
|
||||
enforce idle time which results in more package C-state residency. The
|
||||
user interface is exposed via generic thermal framework.
|
||||
|
||||
+config THERMAL_BCM2835
|
||||
+ tristate "BCM2835 Thermal Driver"
|
||||
+ help
|
||||
+ This will enable temperature monitoring for the Broadcom BCM2835
|
||||
+ chip. If built as a module, it will be called 'bcm2835-thermal'.
|
||||
+
|
||||
config X86_PKG_TEMP_THERMAL
|
||||
tristate "X86 package temperature thermal driver"
|
||||
depends on X86_THERMAL_VECTOR
|
||||
--- a/drivers/thermal/Makefile
|
||||
+++ b/drivers/thermal/Makefile
|
||||
@@ -28,6 +28,7 @@ obj-$(CONFIG_ARMADA_THERMAL) += armada_t
|
||||
obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o
|
||||
obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o
|
||||
obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
|
||||
+obj-$(CONFIG_THERMAL_BCM2835) += bcm2835-thermal.o
|
||||
obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o
|
||||
obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/
|
||||
obj-$(CONFIG_ACPI_INT3403_THERMAL) += int3403_thermal.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/thermal/bcm2835-thermal.c
|
||||
@@ -0,0 +1,184 @@
|
||||
+/*****************************************************************************
|
||||
+* Copyright 2011 Broadcom Corporation. All rights reserved.
|
||||
+*
|
||||
+* Unless you and Broadcom execute a separate written software license
|
||||
+* agreement governing use of this software, this software is licensed to you
|
||||
+* under the terms of the GNU General Public License version 2, available at
|
||||
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
|
||||
+*
|
||||
+* Notwithstanding the above, under no circumstances may you combine this
|
||||
+* software in any way with any other Broadcom software provided under a
|
||||
+* license other than the GPL, without Broadcom's express prior written
|
||||
+* consent.
|
||||
+*****************************************************************************/
|
||||
+
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/sysfs.h>
|
||||
+#include <mach/vcio.h>
|
||||
+#include <linux/thermal.h>
|
||||
+
|
||||
+
|
||||
+/* --- DEFINITIONS --- */
|
||||
+#define MODULE_NAME "bcm2835_thermal"
|
||||
+
|
||||
+/*#define THERMAL_DEBUG_ENABLE*/
|
||||
+
|
||||
+#ifdef THERMAL_DEBUG_ENABLE
|
||||
+#define print_debug(fmt,...) printk(KERN_INFO "%s:%s:%d: "fmt"\n", MODULE_NAME, __func__, __LINE__, ##__VA_ARGS__)
|
||||
+#else
|
||||
+#define print_debug(fmt,...)
|
||||
+#endif
|
||||
+#define print_err(fmt,...) printk(KERN_ERR "%s:%s:%d: "fmt"\n", MODULE_NAME, __func__,__LINE__, ##__VA_ARGS__)
|
||||
+
|
||||
+#define VC_TAG_GET_TEMP 0x00030006
|
||||
+#define VC_TAG_GET_MAX_TEMP 0x0003000A
|
||||
+
|
||||
+typedef enum {
|
||||
+ TEMP,
|
||||
+ MAX_TEMP,
|
||||
+} temp_type;
|
||||
+
|
||||
+/* --- STRUCTS --- */
|
||||
+/* tag part of the message */
|
||||
+struct vc_msg_tag {
|
||||
+ uint32_t tag_id; /* the tag ID for the temperature */
|
||||
+ uint32_t buffer_size; /* size of the buffer (should be 8) */
|
||||
+ uint32_t request_code; /* identifies message as a request (should be 0) */
|
||||
+ uint32_t id; /* extra ID field (should be 0) */
|
||||
+ uint32_t val; /* returned value of the temperature */
|
||||
+};
|
||||
+
|
||||
+/* message structure to be sent to videocore */
|
||||
+struct vc_msg {
|
||||
+ uint32_t msg_size; /* simply, sizeof(struct vc_msg) */
|
||||
+ uint32_t request_code; /* holds various information like the success and number of bytes returned (refer to mailboxes wiki) */
|
||||
+ struct vc_msg_tag tag; /* the tag structure above to make */
|
||||
+ uint32_t end_tag; /* an end identifier, should be set to NULL */
|
||||
+};
|
||||
+
|
||||
+struct bcm2835_thermal_data {
|
||||
+ struct thermal_zone_device *thermal_dev;
|
||||
+ struct vc_msg msg;
|
||||
+};
|
||||
+
|
||||
+/* --- GLOBALS --- */
|
||||
+static struct bcm2835_thermal_data bcm2835_data;
|
||||
+
|
||||
+/* Thermal Device Operations */
|
||||
+static struct thermal_zone_device_ops ops;
|
||||
+
|
||||
+/* --- FUNCTIONS --- */
|
||||
+
|
||||
+static int bcm2835_get_temp_or_max(struct thermal_zone_device *thermal_dev, unsigned long *temp, unsigned tag_id)
|
||||
+{
|
||||
+ int result = -1, retry = 3;
|
||||
+ print_debug("IN");
|
||||
+
|
||||
+ *temp = 0;
|
||||
+ while (result != 0 && retry-- > 0) {
|
||||
+ /* wipe all previous message data */
|
||||
+ memset(&bcm2835_data.msg, 0, sizeof bcm2835_data.msg);
|
||||
+
|
||||
+ /* prepare message */
|
||||
+ bcm2835_data.msg.msg_size = sizeof bcm2835_data.msg;
|
||||
+ bcm2835_data.msg.tag.buffer_size = 8;
|
||||
+ bcm2835_data.msg.tag.tag_id = tag_id;
|
||||
+
|
||||
+ /* send the message */
|
||||
+ result = bcm_mailbox_property(&bcm2835_data.msg, sizeof bcm2835_data.msg);
|
||||
+ print_debug("Got %stemperature as %u (%d,%x)\n", tag_id==VC_TAG_GET_MAX_TEMP ? "max ":"", (uint)bcm2835_data.msg.tag.val, result, bcm2835_data.msg.request_code);
|
||||
+ if (!(bcm2835_data.msg.request_code & 0x80000000))
|
||||
+ result = -1;
|
||||
+ }
|
||||
+
|
||||
+ /* check if it was all ok and return the rate in milli degrees C */
|
||||
+ if (result == 0)
|
||||
+ *temp = (uint)bcm2835_data.msg.tag.val;
|
||||
+ else
|
||||
+ print_err("Failed to get temperature! (%x:%d)\n", tag_id, result);
|
||||
+ print_debug("OUT");
|
||||
+ return result;
|
||||
+}
|
||||
+
|
||||
+static int bcm2835_get_temp(struct thermal_zone_device *thermal_dev, unsigned long *temp)
|
||||
+{
|
||||
+ return bcm2835_get_temp_or_max(thermal_dev, temp, VC_TAG_GET_TEMP);
|
||||
+}
|
||||
+
|
||||
+static int bcm2835_get_max_temp(struct thermal_zone_device *thermal_dev, int trip_num, unsigned long *temp)
|
||||
+{
|
||||
+ return bcm2835_get_temp_or_max(thermal_dev, temp, VC_TAG_GET_MAX_TEMP);
|
||||
+}
|
||||
+
|
||||
+static int bcm2835_get_trip_type(struct thermal_zone_device * thermal_dev, int trip_num, enum thermal_trip_type *trip_type)
|
||||
+{
|
||||
+ *trip_type = THERMAL_TRIP_HOT;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+static int bcm2835_get_mode(struct thermal_zone_device *thermal_dev, enum thermal_device_mode *dev_mode)
|
||||
+{
|
||||
+ *dev_mode = THERMAL_DEVICE_ENABLED;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+static int bcm2835_thermal_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ print_debug("IN");
|
||||
+ print_debug("THERMAL Driver has been probed!");
|
||||
+
|
||||
+ /* check that the device isn't null!*/
|
||||
+ if(pdev == NULL)
|
||||
+ {
|
||||
+ print_debug("Platform device is empty!");
|
||||
+ return -ENODEV;
|
||||
+ }
|
||||
+
|
||||
+ if(!(bcm2835_data.thermal_dev = thermal_zone_device_register("bcm2835_thermal", 1, 0, NULL, &ops, NULL, 0, 0)))
|
||||
+ {
|
||||
+ print_debug("Unable to register the thermal device!");
|
||||
+ return -EFAULT;
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+static int bcm2835_thermal_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ print_debug("IN");
|
||||
+
|
||||
+ thermal_zone_device_unregister(bcm2835_data.thermal_dev);
|
||||
+
|
||||
+ print_debug("OUT");
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static struct thermal_zone_device_ops ops = {
|
||||
+ .get_temp = bcm2835_get_temp,
|
||||
+ .get_trip_temp = bcm2835_get_max_temp,
|
||||
+ .get_trip_type = bcm2835_get_trip_type,
|
||||
+ .get_mode = bcm2835_get_mode,
|
||||
+};
|
||||
+
|
||||
+/* Thermal Driver */
|
||||
+static struct platform_driver bcm2835_thermal_driver = {
|
||||
+ .probe = bcm2835_thermal_probe,
|
||||
+ .remove = bcm2835_thermal_remove,
|
||||
+ .driver = {
|
||||
+ .name = "bcm2835_thermal",
|
||||
+ .owner = THIS_MODULE,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+MODULE_LICENSE("GPL");
|
||||
+MODULE_AUTHOR("Dorian Peake");
|
||||
+MODULE_DESCRIPTION("Thermal driver for bcm2835 chip");
|
||||
+
|
||||
+module_platform_driver(bcm2835_thermal_driver);
|
|
@ -1,91 +0,0 @@
|
|||
From c025fdfc5970f5d60a381bab953e60825de8500a Mon Sep 17 00:00:00 2001
|
||||
From: popcornmix <popcornmix@gmail.com>
|
||||
Date: Tue, 26 Mar 2013 17:26:38 +0000
|
||||
Subject: [PATCH 16/54] Allow mac address to be set in smsc95xx
|
||||
|
||||
Signed-off-by: popcornmix <popcornmix@gmail.com>
|
||||
---
|
||||
drivers/net/usb/smsc95xx.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++
|
||||
1 file changed, 56 insertions(+)
|
||||
|
||||
--- a/drivers/net/usb/smsc95xx.c
|
||||
+++ b/drivers/net/usb/smsc95xx.c
|
||||
@@ -59,6 +59,7 @@
|
||||
#define SUSPEND_SUSPEND3 (0x08)
|
||||
#define SUSPEND_ALLMODES (SUSPEND_SUSPEND0 | SUSPEND_SUSPEND1 | \
|
||||
SUSPEND_SUSPEND2 | SUSPEND_SUSPEND3)
|
||||
+#define MAC_ADDR_LEN (6)
|
||||
|
||||
struct smsc95xx_priv {
|
||||
u32 mac_cr;
|
||||
@@ -74,6 +75,10 @@ static bool turbo_mode = true;
|
||||
module_param(turbo_mode, bool, 0644);
|
||||
MODULE_PARM_DESC(turbo_mode, "Enable multiple frames per Rx transaction");
|
||||
|
||||
+static char *macaddr = ":";
|
||||
+module_param(macaddr, charp, 0);
|
||||
+MODULE_PARM_DESC(macaddr, "MAC address");
|
||||
+
|
||||
static int __must_check __smsc95xx_read_reg(struct usbnet *dev, u32 index,
|
||||
u32 *data, int in_pm)
|
||||
{
|
||||
@@ -763,8 +768,59 @@ static int smsc95xx_ioctl(struct net_dev
|
||||
return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL);
|
||||
}
|
||||
|
||||
+/* Check the macaddr module parameter for a MAC address */
|
||||
+static int smsc95xx_is_macaddr_param(struct usbnet *dev, u8 *dev_mac)
|
||||
+{
|
||||
+ int i, j, got_num, num;
|
||||
+ u8 mtbl[MAC_ADDR_LEN];
|
||||
+
|
||||
+ if (macaddr[0] == ':')
|
||||
+ return 0;
|
||||
+
|
||||
+ i = 0;
|
||||
+ j = 0;
|
||||
+ num = 0;
|
||||
+ got_num = 0;
|
||||
+ while (j < MAC_ADDR_LEN) {
|
||||
+ if (macaddr[i] && macaddr[i] != ':') {
|
||||
+ got_num++;
|
||||
+ if ('0' <= macaddr[i] && macaddr[i] <= '9')
|
||||
+ num = num * 16 + macaddr[i] - '0';
|
||||
+ else if ('A' <= macaddr[i] && macaddr[i] <= 'F')
|
||||
+ num = num * 16 + 10 + macaddr[i] - 'A';
|
||||
+ else if ('a' <= macaddr[i] && macaddr[i] <= 'f')
|
||||
+ num = num * 16 + 10 + macaddr[i] - 'a';
|
||||
+ else
|
||||
+ break;
|
||||
+ i++;
|
||||
+ } else if (got_num == 2) {
|
||||
+ mtbl[j++] = (u8) num;
|
||||
+ num = 0;
|
||||
+ got_num = 0;
|
||||
+ i++;
|
||||
+ } else {
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (j == MAC_ADDR_LEN) {
|
||||
+ netif_dbg(dev, ifup, dev->net, "Overriding MAC address with: "
|
||||
+ "%02x:%02x:%02x:%02x:%02x:%02x\n", mtbl[0], mtbl[1], mtbl[2],
|
||||
+ mtbl[3], mtbl[4], mtbl[5]);
|
||||
+ for (i = 0; i < MAC_ADDR_LEN; i++)
|
||||
+ dev_mac[i] = mtbl[i];
|
||||
+ return 1;
|
||||
+ } else {
|
||||
+ return 0;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
static void smsc95xx_init_mac_address(struct usbnet *dev)
|
||||
{
|
||||
+ /* Check module parameters */
|
||||
+ if (smsc95xx_is_macaddr_param(dev, dev->net->dev_addr))
|
||||
+ return;
|
||||
+
|
||||
/* try reading mac address from EEPROM */
|
||||
if (smsc95xx_read_eeprom(dev, EEPROM_MAC_OFFSET, ETH_ALEN,
|
||||
dev->net->dev_addr) == 0) {
|
File diff suppressed because it is too large
Load diff
|
@ -1,72 +0,0 @@
|
|||
From d002a24f7ff4ca8b63d08e33fd2d88af84501267 Mon Sep 17 00:00:00 2001
|
||||
From: cbeytas <cbeytas@shaw.ca>
|
||||
Date: Mon, 24 Jun 2013 00:05:40 -0400
|
||||
Subject: [PATCH 18/54] Perform I2C combined transactions when possible
|
||||
|
||||
Perform I2C combined transactions whenever possible, within the
|
||||
restrictions of the Broadcomm Serial Controller.
|
||||
|
||||
Disable DONE interrupt during TA poll
|
||||
|
||||
Prevent interrupt from being triggered if poll is missed and transfer
|
||||
starts and finishes.
|
||||
|
||||
i2c: Make combined transactions optional and disabled by default
|
||||
---
|
||||
drivers/i2c/busses/i2c-bcm2708.c | 31 ++++++++++++++++++++++++++++++-
|
||||
1 file changed, 30 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/i2c/busses/i2c-bcm2708.c
|
||||
+++ b/drivers/i2c/busses/i2c-bcm2708.c
|
||||
@@ -74,6 +74,9 @@ static unsigned int baudrate = CONFIG_I2
|
||||
module_param(baudrate, uint, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
|
||||
MODULE_PARM_DESC(baudrate, "The I2C baudrate");
|
||||
|
||||
+static bool combined = false;
|
||||
+module_param(combined, bool, 0644);
|
||||
+MODULE_PARM_DESC(combined, "Use combined transactions");
|
||||
|
||||
struct bcm2708_i2c {
|
||||
struct i2c_adapter adapter;
|
||||
@@ -150,7 +153,7 @@ static inline void bcm2708_bsc_fifo_fill
|
||||
static inline void bcm2708_bsc_setup(struct bcm2708_i2c *bi)
|
||||
{
|
||||
unsigned long bus_hz;
|
||||
- u32 cdiv;
|
||||
+ u32 cdiv, s;
|
||||
u32 c = BSC_C_I2CEN | BSC_C_INTD | BSC_C_ST | BSC_C_CLEAR_1;
|
||||
|
||||
bus_hz = clk_get_rate(bi->clk);
|
||||
@@ -166,6 +169,32 @@ static inline void bcm2708_bsc_setup(str
|
||||
bcm2708_wr(bi, BSC_DIV, cdiv);
|
||||
bcm2708_wr(bi, BSC_A, bi->msg->addr);
|
||||
bcm2708_wr(bi, BSC_DLEN, bi->msg->len);
|
||||
+ if (combined)
|
||||
+ {
|
||||
+ /* Do the next two messages meet combined transaction criteria?
|
||||
+ - Current message is a write, next message is a read
|
||||
+ - Both messages to same slave address
|
||||
+ - Write message can fit inside FIFO (16 bytes or less) */
|
||||
+ if ( (bi->nmsgs > 1) &&
|
||||
+ !(bi->msg[0].flags & I2C_M_RD) && (bi->msg[1].flags & I2C_M_RD) &&
|
||||
+ (bi->msg[0].addr == bi->msg[1].addr) && (bi->msg[0].len <= 16)) {
|
||||
+ /* Fill FIFO with entire write message (16 byte FIFO) */
|
||||
+ while (bi->pos < bi->msg->len)
|
||||
+ bcm2708_wr(bi, BSC_FIFO, bi->msg->buf[bi->pos++]);
|
||||
+ /* Start write transfer (no interrupts, don't clear FIFO) */
|
||||
+ bcm2708_wr(bi, BSC_C, BSC_C_I2CEN | BSC_C_ST);
|
||||
+ /* poll for transfer start bit (should only take 1-20 polls) */
|
||||
+ do {
|
||||
+ s = bcm2708_rd(bi, BSC_S);
|
||||
+ } while (!(s & (BSC_S_TA | BSC_S_ERR | BSC_S_CLKT | BSC_S_DONE)));
|
||||
+ /* Send next read message before the write transfer finishes. */
|
||||
+ bi->nmsgs--;
|
||||
+ bi->msg++;
|
||||
+ bi->pos = 0;
|
||||
+ bcm2708_wr(bi, BSC_DLEN, bi->msg->len);
|
||||
+ c = BSC_C_I2CEN | BSC_C_INTD | BSC_C_INTR | BSC_C_ST | BSC_C_READ;
|
||||
+ }
|
||||
+ }
|
||||
bcm2708_wr(bi, BSC_C, c);
|
||||
}
|
||||
|
|
@ -1,266 +0,0 @@
|
|||
From 370acd9248703fb8531cd87982fb02dcce32a2b4 Mon Sep 17 00:00:00 2001
|
||||
From: popcornmix <popcornmix@gmail.com>
|
||||
Date: Wed, 8 May 2013 11:46:50 +0100
|
||||
Subject: [PATCH 19/54] enabling the realtime clock 1-wire chip DS1307 and
|
||||
1-wire on GPIO4 (as a module)
|
||||
|
||||
1-wire: Add support for configuring pin for w1-gpio kernel module
|
||||
See: https://github.com/raspberrypi/linux/pull/457
|
||||
|
||||
Add bitbanging pullups, use them for w1-gpio
|
||||
|
||||
Allows parasite power to work, uses module option pullup=1
|
||||
|
||||
bcm2708: Ensure 1-wire pullup is disabled by default, and expose as module parameter
|
||||
|
||||
Signed-off-by: Alex J Lennon <ajlennon@dynamicdevices.co.uk>
|
||||
|
||||
w1-gpio: Add gpiopin module parameter and correctly free up gpio pull-up pin, if set
|
||||
|
||||
Signed-off-by: Alex J Lennon <ajlennon@dynamicdevices.co.uk>
|
||||
---
|
||||
arch/arm/mach-bcm2708/bcm2708.c | 29 +++++++++++++++++++++
|
||||
drivers/w1/masters/w1-gpio.c | 57 ++++++++++++++++++++++++++++++++++++-----
|
||||
drivers/w1/w1.h | 6 +++++
|
||||
drivers/w1/w1_int.c | 14 ++++++++++
|
||||
drivers/w1/w1_io.c | 18 ++++++++++---
|
||||
5 files changed, 115 insertions(+), 9 deletions(-)
|
||||
|
||||
--- a/arch/arm/mach-bcm2708/bcm2708.c
|
||||
+++ b/arch/arm/mach-bcm2708/bcm2708.c
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
+#include <linux/w1-gpio.h>
|
||||
|
||||
#include <linux/version.h>
|
||||
#include <linux/clkdev.h>
|
||||
@@ -76,12 +77,19 @@
|
||||
*/
|
||||
#define DMA_MASK_BITS_COMMON 32
|
||||
|
||||
+// use GPIO 4 for the one-wire GPIO pin, if enabled
|
||||
+#define W1_GPIO 4
|
||||
+// ensure one-wire GPIO pullup is disabled by default
|
||||
+#define W1_PULLUP -1
|
||||
+
|
||||
/* command line parameters */
|
||||
static unsigned boardrev, serial;
|
||||
static unsigned uart_clock;
|
||||
static unsigned disk_led_gpio = 16;
|
||||
static unsigned disk_led_active_low = 1;
|
||||
static unsigned reboot_part = 0;
|
||||
+static unsigned w1_gpio_pin = W1_GPIO;
|
||||
+static unsigned w1_gpio_pullup = W1_PULLUP;
|
||||
|
||||
static void __init bcm2708_init_led(void);
|
||||
|
||||
@@ -258,6 +266,20 @@ static struct platform_device bcm2708_dm
|
||||
.num_resources = ARRAY_SIZE(bcm2708_dmaman_resources),
|
||||
};
|
||||
|
||||
+#if defined(CONFIG_W1_MASTER_GPIO) || defined(CONFIG_W1_MASTER_GPIO_MODULE)
|
||||
+static struct w1_gpio_platform_data w1_gpio_pdata = {
|
||||
+ .pin = W1_GPIO,
|
||||
+ .ext_pullup_enable_pin = W1_PULLUP,
|
||||
+ .is_open_drain = 0,
|
||||
+};
|
||||
+
|
||||
+static struct platform_device w1_device = {
|
||||
+ .name = "w1-gpio",
|
||||
+ .id = -1,
|
||||
+ .dev.platform_data = &w1_gpio_pdata,
|
||||
+};
|
||||
+#endif
|
||||
+
|
||||
static u64 fb_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON);
|
||||
|
||||
static struct platform_device bcm2708_fb_device = {
|
||||
@@ -680,6 +702,11 @@ void __init bcm2708_init(void)
|
||||
#ifdef CONFIG_BCM2708_GPIO
|
||||
bcm_register_device(&bcm2708_gpio_device);
|
||||
#endif
|
||||
+#if defined(CONFIG_W1_MASTER_GPIO) || defined(CONFIG_W1_MASTER_GPIO_MODULE)
|
||||
+ w1_gpio_pdata.pin = w1_gpio_pin;
|
||||
+ w1_gpio_pdata.ext_pullup_enable_pin = w1_gpio_pullup;
|
||||
+ platform_device_register(&w1_device);
|
||||
+#endif
|
||||
bcm_register_device(&bcm2708_systemtimer_device);
|
||||
bcm_register_device(&bcm2708_fb_device);
|
||||
bcm_register_device(&bcm2708_usb_device);
|
||||
@@ -883,3 +910,5 @@ module_param(uart_clock, uint, 0644);
|
||||
module_param(disk_led_gpio, uint, 0644);
|
||||
module_param(disk_led_active_low, uint, 0644);
|
||||
module_param(reboot_part, uint, 0644);
|
||||
+module_param(w1_gpio_pin, uint, 0644);
|
||||
+module_param(w1_gpio_pullup, uint, 0644);
|
||||
--- a/drivers/w1/masters/w1-gpio.c
|
||||
+++ b/drivers/w1/masters/w1-gpio.c
|
||||
@@ -23,6 +23,15 @@
|
||||
#include "../w1.h"
|
||||
#include "../w1_int.h"
|
||||
|
||||
+static int w1_gpio_pullup = -1;
|
||||
+static int w1_gpio_pullup_orig = -1;
|
||||
+module_param_named(pullup, w1_gpio_pullup, int, 0);
|
||||
+MODULE_PARM_DESC(pullup, "GPIO pin pullup number");
|
||||
+static int w1_gpio_pin = -1;
|
||||
+static int w1_gpio_pin_orig = -1;
|
||||
+module_param_named(gpiopin, w1_gpio_pin, int, 0);
|
||||
+MODULE_PARM_DESC(gpiopin, "GPIO pin number");
|
||||
+
|
||||
static u8 w1_gpio_set_pullup(void *data, int delay)
|
||||
{
|
||||
struct w1_gpio_platform_data *pdata = data;
|
||||
@@ -67,6 +76,16 @@ static u8 w1_gpio_read_bit(void *data)
|
||||
return gpio_get_value(pdata->pin) ? 1 : 0;
|
||||
}
|
||||
|
||||
+static void w1_gpio_bitbang_pullup(void *data, u8 on)
|
||||
+{
|
||||
+ struct w1_gpio_platform_data *pdata = data;
|
||||
+
|
||||
+ if (on)
|
||||
+ gpio_direction_output(pdata->pin, 1);
|
||||
+ else
|
||||
+ gpio_direction_input(pdata->pin);
|
||||
+}
|
||||
+
|
||||
#if defined(CONFIG_OF)
|
||||
static struct of_device_id w1_gpio_dt_ids[] = {
|
||||
{ .compatible = "w1-gpio" },
|
||||
@@ -102,14 +121,16 @@ static int w1_gpio_probe_dt(struct platf
|
||||
static int w1_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct w1_bus_master *master;
|
||||
- struct w1_gpio_platform_data *pdata;
|
||||
+ struct w1_gpio_platform_data *pdata = pdev->dev.platform_data;
|
||||
int err;
|
||||
|
||||
- if (of_have_populated_dt()) {
|
||||
- err = w1_gpio_probe_dt(pdev);
|
||||
- if (err < 0) {
|
||||
- dev_err(&pdev->dev, "Failed to parse DT\n");
|
||||
- return err;
|
||||
+ if(pdata == NULL) {
|
||||
+ if (of_have_populated_dt()) {
|
||||
+ err = w1_gpio_probe_dt(pdev);
|
||||
+ if (err < 0) {
|
||||
+ dev_err(&pdev->dev, "Failed to parse DT\n");
|
||||
+ return err;
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,6 +148,19 @@ static int w1_gpio_probe(struct platform
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
+ w1_gpio_pin_orig = pdata->pin;
|
||||
+ w1_gpio_pullup_orig = pdata->ext_pullup_enable_pin;
|
||||
+
|
||||
+ if(gpio_is_valid(w1_gpio_pin)) {
|
||||
+ pdata->pin = w1_gpio_pin;
|
||||
+ pdata->ext_pullup_enable_pin = -1;
|
||||
+ }
|
||||
+ if(gpio_is_valid(w1_gpio_pullup)) {
|
||||
+ pdata->ext_pullup_enable_pin = w1_gpio_pullup;
|
||||
+ }
|
||||
+
|
||||
+ dev_info(&pdev->dev, "gpio pin %d, gpio pullup pin %d\n", pdata->pin, pdata->ext_pullup_enable_pin);
|
||||
+
|
||||
err = devm_gpio_request(&pdev->dev, pdata->pin, "w1");
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "gpio_request (pin) failed\n");
|
||||
@@ -156,6 +190,14 @@ static int w1_gpio_probe(struct platform
|
||||
master->set_pullup = w1_gpio_set_pullup;
|
||||
}
|
||||
|
||||
+ if (gpio_is_valid(w1_gpio_pullup)) {
|
||||
+ if (pdata->is_open_drain)
|
||||
+ printk(KERN_ERR "w1-gpio 'pullup' option "
|
||||
+ "doesn't work with open drain GPIO\n");
|
||||
+ else
|
||||
+ master->bitbang_pullup = w1_gpio_bitbang_pullup;
|
||||
+ }
|
||||
+
|
||||
err = w1_add_master_device(master);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "w1_add_master device failed\n");
|
||||
@@ -186,6 +228,9 @@ static int w1_gpio_remove(struct platfor
|
||||
|
||||
w1_remove_master_device(master);
|
||||
|
||||
+ pdata->pin = w1_gpio_pin_orig;
|
||||
+ pdata->ext_pullup_enable_pin = w1_gpio_pullup_orig;
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
|
||||
--- a/drivers/w1/w1.h
|
||||
+++ b/drivers/w1/w1.h
|
||||
@@ -148,6 +148,12 @@ struct w1_bus_master
|
||||
*/
|
||||
u8 (*set_pullup)(void *, int);
|
||||
|
||||
+ /**
|
||||
+ * Turns the pullup on/off in bitbanging mode, takes an on/off argument.
|
||||
+ * @return -1=Error, 0=completed
|
||||
+ */
|
||||
+ void (*bitbang_pullup) (void *, u8);
|
||||
+
|
||||
/** Really nice hardware can handles the different types of ROM search
|
||||
* w1_master* is passed to the slave found callback.
|
||||
*/
|
||||
--- a/drivers/w1/w1_int.c
|
||||
+++ b/drivers/w1/w1_int.c
|
||||
@@ -118,6 +118,20 @@ int w1_add_master_device(struct w1_bus_m
|
||||
return(-EINVAL);
|
||||
}
|
||||
|
||||
+ /* bitbanging hardware uses bitbang_pullup, other hardware uses set_pullup
|
||||
+ * and takes care of timing itself */
|
||||
+ if (!master->write_byte && !master->touch_bit && master->set_pullup) {
|
||||
+ printk(KERN_ERR "w1_add_master_device: set_pullup requires "
|
||||
+ "write_byte or touch_bit, disabling\n");
|
||||
+ master->set_pullup = NULL;
|
||||
+ }
|
||||
+
|
||||
+ if (master->set_pullup && master->bitbang_pullup) {
|
||||
+ printk(KERN_ERR "w1_add_master_device: set_pullup should not "
|
||||
+ "be set when bitbang_pullup is used, disabling\n");
|
||||
+ master->set_pullup = NULL;
|
||||
+ }
|
||||
+
|
||||
/* Lock until the device is added (or not) to w1_masters. */
|
||||
mutex_lock(&w1_mlock);
|
||||
/* Search for the first available id (starting at 1). */
|
||||
--- a/drivers/w1/w1_io.c
|
||||
+++ b/drivers/w1/w1_io.c
|
||||
@@ -127,10 +127,22 @@ static void w1_pre_write(struct w1_maste
|
||||
static void w1_post_write(struct w1_master *dev)
|
||||
{
|
||||
if (dev->pullup_duration) {
|
||||
- if (dev->enable_pullup && dev->bus_master->set_pullup)
|
||||
- dev->bus_master->set_pullup(dev->bus_master->data, 0);
|
||||
- else
|
||||
+ if (dev->enable_pullup) {
|
||||
+ if (dev->bus_master->set_pullup) {
|
||||
+ dev->bus_master->set_pullup(dev->
|
||||
+ bus_master->data,
|
||||
+ 0);
|
||||
+ } else if (dev->bus_master->bitbang_pullup) {
|
||||
+ dev->bus_master->
|
||||
+ bitbang_pullup(dev->bus_master->data, 1);
|
||||
+ msleep(dev->pullup_duration);
|
||||
+ dev->bus_master->
|
||||
+ bitbang_pullup(dev->bus_master->data, 0);
|
||||
+ }
|
||||
+ } else {
|
||||
msleep(dev->pullup_duration);
|
||||
+ }
|
||||
+
|
||||
dev->pullup_duration = 0;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,99 +0,0 @@
|
|||
From eaaa194153e61ed6f9fec8dda18f3e462187d327 Mon Sep 17 00:00:00 2001
|
||||
From: popcornmix <popcornmix@gmail.com>
|
||||
Date: Sat, 9 Nov 2013 13:32:03 +0000
|
||||
Subject: [PATCH 21/54] wifi: add patches from 3.6.y tree to make rtl8192cu
|
||||
work
|
||||
|
||||
wifi: add missing patch from 3.6.y tree to disable debug
|
||||
---
|
||||
drivers/net/wireless/rtl8192cu/Kconfig | 4 +++-
|
||||
drivers/net/wireless/rtl8192cu/Makefile | 6 +++++-
|
||||
drivers/net/wireless/rtl8192cu/include/autoconf.h | 2 +-
|
||||
drivers/net/wireless/rtl8192cu/os_dep/linux/os_intfs.c | 13 +++++++++++++
|
||||
drivers/net/wireless/rtl8192cu/os_dep/linux/usb_intf.c | 1 +
|
||||
5 files changed, 23 insertions(+), 3 deletions(-)
|
||||
|
||||
--- a/drivers/net/wireless/rtl8192cu/Kconfig
|
||||
+++ b/drivers/net/wireless/rtl8192cu/Kconfig
|
||||
@@ -1,6 +1,8 @@
|
||||
config RTL8192CU
|
||||
tristate "Realtek 8192C USB WiFi"
|
||||
depends on USB
|
||||
+ select WIRELESS_EXT
|
||||
+ select WEXT_PRIV
|
||||
---help---
|
||||
- Help message of RTL8192CU
|
||||
+ Enable wireless network adapters based on Realtek RTL8192C chipset family, such as EDUP nano series
|
||||
|
||||
--- a/drivers/net/wireless/rtl8192cu/Makefile
|
||||
+++ b/drivers/net/wireless/rtl8192cu/Makefile
|
||||
@@ -38,7 +38,7 @@ CONFIG_RTL8192CU_REDEFINE_1X1 = n
|
||||
CONFIG_INTEL_WIDI = n
|
||||
CONFIG_WAKE_ON_WLAN = n
|
||||
|
||||
-CONFIG_PLATFORM_I386_PC = y
|
||||
+CONFIG_PLATFORM_I386_PC = n
|
||||
CONFIG_PLATFORM_TI_AM3517 = n
|
||||
CONFIG_PLATFORM_ANDROID_X86 = n
|
||||
CONFIG_PLATFORM_JB_X86 = n
|
||||
@@ -524,6 +524,10 @@ KVER := 3.3.0
|
||||
#KSRC:= ../lichee/linux-3.3/
|
||||
endif
|
||||
|
||||
+ifeq ($(CONFIG_ARCH_BCM2708), y)
|
||||
+EXTRA_CFLAGS += -DCONFIG_LITTLE_ENDIAN
|
||||
+endif
|
||||
+
|
||||
ifneq ($(USER_MODULE_NAME),)
|
||||
MODULE_NAME := $(USER_MODULE_NAME)
|
||||
endif
|
||||
--- a/drivers/net/wireless/rtl8192cu/include/autoconf.h
|
||||
+++ b/drivers/net/wireless/rtl8192cu/include/autoconf.h
|
||||
@@ -296,7 +296,7 @@
|
||||
//#define CONFIG_DEBUG_RTL871X
|
||||
|
||||
#define DBG 0
|
||||
-#define CONFIG_DEBUG_RTL819X
|
||||
+//#define CONFIG_DEBUG_RTL819X
|
||||
|
||||
#define CONFIG_PROC_DEBUG 1
|
||||
|
||||
--- a/drivers/net/wireless/rtl8192cu/os_dep/linux/os_intfs.c
|
||||
+++ b/drivers/net/wireless/rtl8192cu/os_dep/linux/os_intfs.c
|
||||
@@ -277,6 +277,18 @@ static int rtw_proc_cnt = 0;
|
||||
|
||||
#define RTW_PROC_NAME DRV_NAME
|
||||
|
||||
+#ifndef create_proc_entry
|
||||
+/* dummy routines */
|
||||
+void rtw_proc_remove_one(struct net_device *dev)
|
||||
+{
|
||||
+}
|
||||
+
|
||||
+void rtw_proc_init_one(struct net_device *dev)
|
||||
+{
|
||||
+}
|
||||
+
|
||||
+#else /* create_proc_entry not defined */
|
||||
+
|
||||
void rtw_proc_init_one(struct net_device *dev)
|
||||
{
|
||||
struct proc_dir_entry *dir_dev = NULL;
|
||||
@@ -751,6 +763,7 @@ void rtw_proc_remove_one(struct net_devi
|
||||
}
|
||||
}
|
||||
}
|
||||
+#endif /* create_proc_entry not defined */
|
||||
#endif
|
||||
|
||||
uint loadparam( _adapter *padapter, _nic_hdl pnetdev);
|
||||
--- a/drivers/net/wireless/rtl8192cu/os_dep/linux/usb_intf.c
|
||||
+++ b/drivers/net/wireless/rtl8192cu/os_dep/linux/usb_intf.c
|
||||
@@ -138,6 +138,7 @@ static void rtw_dev_remove(struct usb_in
|
||||
{USB_DEVICE(0x2001, 0x3307)},/* D-Link - Cameo */ \
|
||||
{USB_DEVICE(0x2001, 0x330A)},/* D-Link - Alpha */ \
|
||||
{USB_DEVICE(0x2001, 0x3309)},/* D-Link - Alpha */ \
|
||||
+ {USB_DEVICE(0x2001, 0x330D)},/* D-Link - Alpha(?) */ \
|
||||
{USB_DEVICE(0x0586, 0x341F)},/* Zyxel - Abocom */ \
|
||||
{USB_DEVICE(0x7392, 0x7822)},/* Edimax - Edimax */ \
|
||||
{USB_DEVICE(0x2019, 0xAB2B)},/* Planex - Abocom */ \
|
|
@ -1,22 +0,0 @@
|
|||
From 3803724f9befcbaba097c6c052f00959dd3813cb Mon Sep 17 00:00:00 2001
|
||||
From: popcornmix <popcornmix@gmail.com>
|
||||
Date: Wed, 3 Jul 2013 00:54:08 +0100
|
||||
Subject: [PATCH 22/54] Added Device IDs for August DVB-T 205
|
||||
|
||||
---
|
||||
drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 4 ++++
|
||||
1 file changed, 4 insertions(+)
|
||||
|
||||
--- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
|
||||
+++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
|
||||
@@ -1429,6 +1429,10 @@ static const struct usb_device_id rtl28x
|
||||
&rtl2832u_props, "Compro VideoMate U620F", NULL) },
|
||||
{ DVB_USB_DEVICE(USB_VID_KWORLD_2, 0xd394,
|
||||
&rtl2832u_props, "MaxMedia HU394-T", NULL) },
|
||||
+ { DVB_USB_DEVICE(USB_VID_GTEK, 0xb803 /*USB_PID_AUGUST_DVBT205*/,
|
||||
+ &rtl2832u_props, "August DVB-T 205", NULL) },
|
||||
+ { DVB_USB_DEVICE(USB_VID_GTEK, 0xa803 /*USB_PID_AUGUST_DVBT205*/,
|
||||
+ &rtl2832u_props, "August DVB-T 205", NULL) },
|
||||
{ DVB_USB_DEVICE(USB_VID_LEADTEK, 0x6a03,
|
||||
&rtl2832u_props, "Leadtek WinFast DTV Dongle mini", NULL) },
|
||||
{ DVB_USB_DEVICE(USB_VID_GTEK, USB_PID_CPYTO_REDI_PC50A,
|
File diff suppressed because it is too large
Load diff
|
@ -1,209 +0,0 @@
|
|||
From baadd46e587d311e40cfdcec7bdf691c8cce7fa9 Mon Sep 17 00:00:00 2001
|
||||
From: Harm Hanemaaijer <fgenfb@yahoo.com>
|
||||
Date: Thu, 20 Jun 2013 20:21:39 +0200
|
||||
Subject: [PATCH 24/54] Speed up console framebuffer imageblit function
|
||||
|
||||
Especially on platforms with a slower CPU but a relatively high
|
||||
framebuffer fill bandwidth, like current ARM devices, the existing
|
||||
console monochrome imageblit function used to draw console text is
|
||||
suboptimal for common pixel depths such as 16bpp and 32bpp. The existing
|
||||
code is quite general and can deal with several pixel depths. By creating
|
||||
special case functions for 16bpp and 32bpp, by far the most common pixel
|
||||
formats used on modern systems, a significant speed-up is attained
|
||||
which can be readily felt on ARM-based devices like the Raspberry Pi
|
||||
and the Allwinner platform, but should help any platform using the
|
||||
fb layer.
|
||||
|
||||
The special case functions allow constant folding, eliminating a number
|
||||
of instructions including divide operations, and allow the use of an
|
||||
unrolled loop, eliminating instructions with a variable shift size,
|
||||
reducing source memory access instructions, and eliminating excessive
|
||||
branching. These unrolled loops also allow much better code optimization
|
||||
by the C compiler. The code that selects which optimized variant is used
|
||||
is also simplified, eliminating integer divide instructions.
|
||||
|
||||
The speed-up, measured by timing 'cat file.txt' in the console, varies
|
||||
between 40% and 70%, when testing on the Raspberry Pi and Allwinner
|
||||
ARM-based platforms, depending on font size and the pixel depth, with
|
||||
the greater benefit for 32bpp.
|
||||
|
||||
Signed-off-by: Harm Hanemaaijer <fgenfb@yahoo.com>
|
||||
---
|
||||
drivers/video/cfbimgblt.c | 152 ++++++++++++++++++++++++++++++++++++++++++++--
|
||||
1 file changed, 147 insertions(+), 5 deletions(-)
|
||||
|
||||
--- a/drivers/video/cfbimgblt.c
|
||||
+++ b/drivers/video/cfbimgblt.c
|
||||
@@ -28,6 +28,11 @@
|
||||
*
|
||||
* Also need to add code to deal with cards endians that are different than
|
||||
* the native cpu endians. I also need to deal with MSB position in the word.
|
||||
+ * Modified by Harm Hanemaaijer (fgenfb@yahoo.com) 2013:
|
||||
+ * - Provide optimized versions of fast_imageblit for 16 and 32bpp that are
|
||||
+ * significantly faster than the previous implementation.
|
||||
+ * - Simplify the fast/slow_imageblit selection code, avoiding integer
|
||||
+ * divides.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/string.h>
|
||||
@@ -262,6 +267,133 @@ static inline void fast_imageblit(const
|
||||
}
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Optimized fast_imageblit for bpp == 16. ppw = 2, bit_mask = 3 folded
|
||||
+ * into the code, main loop unrolled.
|
||||
+ */
|
||||
+
|
||||
+static inline void fast_imageblit16(const struct fb_image *image,
|
||||
+ struct fb_info *p, u8 __iomem * dst1,
|
||||
+ u32 fgcolor, u32 bgcolor)
|
||||
+{
|
||||
+ u32 fgx = fgcolor, bgx = bgcolor;
|
||||
+ u32 spitch = (image->width + 7) / 8;
|
||||
+ u32 end_mask, eorx;
|
||||
+ const char *s = image->data, *src;
|
||||
+ u32 __iomem *dst;
|
||||
+ const u32 *tab = NULL;
|
||||
+ int i, j, k;
|
||||
+
|
||||
+ tab = fb_be_math(p) ? cfb_tab16_be : cfb_tab16_le;
|
||||
+
|
||||
+ fgx <<= 16;
|
||||
+ bgx <<= 16;
|
||||
+ fgx |= fgcolor;
|
||||
+ bgx |= bgcolor;
|
||||
+
|
||||
+ eorx = fgx ^ bgx;
|
||||
+ k = image->width / 2;
|
||||
+
|
||||
+ for (i = image->height; i--;) {
|
||||
+ dst = (u32 __iomem *) dst1;
|
||||
+ src = s;
|
||||
+
|
||||
+ j = k;
|
||||
+ while (j >= 4) {
|
||||
+ u8 bits = *src;
|
||||
+ end_mask = tab[(bits >> 6) & 3];
|
||||
+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
|
||||
+ end_mask = tab[(bits >> 4) & 3];
|
||||
+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
|
||||
+ end_mask = tab[(bits >> 2) & 3];
|
||||
+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
|
||||
+ end_mask = tab[bits & 3];
|
||||
+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
|
||||
+ src++;
|
||||
+ j -= 4;
|
||||
+ }
|
||||
+ if (j != 0) {
|
||||
+ u8 bits = *src;
|
||||
+ end_mask = tab[(bits >> 6) & 3];
|
||||
+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
|
||||
+ if (j >= 2) {
|
||||
+ end_mask = tab[(bits >> 4) & 3];
|
||||
+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
|
||||
+ if (j == 3) {
|
||||
+ end_mask = tab[(bits >> 2) & 3];
|
||||
+ FB_WRITEL((end_mask & eorx) ^ bgx, dst);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ dst1 += p->fix.line_length;
|
||||
+ s += spitch;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Optimized fast_imageblit for bpp == 32. ppw = 1, bit_mask = 1 folded
|
||||
+ * into the code, main loop unrolled.
|
||||
+ */
|
||||
+
|
||||
+static inline void fast_imageblit32(const struct fb_image *image,
|
||||
+ struct fb_info *p, u8 __iomem * dst1,
|
||||
+ u32 fgcolor, u32 bgcolor)
|
||||
+{
|
||||
+ u32 fgx = fgcolor, bgx = bgcolor;
|
||||
+ u32 spitch = (image->width + 7) / 8;
|
||||
+ u32 end_mask, eorx;
|
||||
+ const char *s = image->data, *src;
|
||||
+ u32 __iomem *dst;
|
||||
+ const u32 *tab = NULL;
|
||||
+ int i, j, k;
|
||||
+
|
||||
+ tab = cfb_tab32;
|
||||
+
|
||||
+ eorx = fgx ^ bgx;
|
||||
+ k = image->width;
|
||||
+
|
||||
+ for (i = image->height; i--;) {
|
||||
+ dst = (u32 __iomem *) dst1;
|
||||
+ src = s;
|
||||
+
|
||||
+ j = k;
|
||||
+ while (j >= 8) {
|
||||
+ u8 bits = *src;
|
||||
+ end_mask = tab[(bits >> 7) & 1];
|
||||
+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
|
||||
+ end_mask = tab[(bits >> 6) & 1];
|
||||
+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
|
||||
+ end_mask = tab[(bits >> 5) & 1];
|
||||
+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
|
||||
+ end_mask = tab[(bits >> 4) & 1];
|
||||
+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
|
||||
+ end_mask = tab[(bits >> 3) & 1];
|
||||
+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
|
||||
+ end_mask = tab[(bits >> 2) & 1];
|
||||
+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
|
||||
+ end_mask = tab[(bits >> 1) & 1];
|
||||
+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
|
||||
+ end_mask = tab[bits & 1];
|
||||
+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
|
||||
+ src++;
|
||||
+ j -= 8;
|
||||
+ }
|
||||
+ if (j != 0) {
|
||||
+ u32 bits = (u32) * src;
|
||||
+ while (j > 1) {
|
||||
+ end_mask = tab[(bits >> 7) & 1];
|
||||
+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
|
||||
+ bits <<= 1;
|
||||
+ j--;
|
||||
+ }
|
||||
+ end_mask = tab[(bits >> 7) & 1];
|
||||
+ FB_WRITEL((end_mask & eorx) ^ bgx, dst);
|
||||
+ }
|
||||
+ dst1 += p->fix.line_length;
|
||||
+ s += spitch;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
void cfb_imageblit(struct fb_info *p, const struct fb_image *image)
|
||||
{
|
||||
u32 fgcolor, bgcolor, start_index, bitstart, pitch_index = 0;
|
||||
@@ -294,11 +426,21 @@ void cfb_imageblit(struct fb_info *p, co
|
||||
bgcolor = image->bg_color;
|
||||
}
|
||||
|
||||
- if (32 % bpp == 0 && !start_index && !pitch_index &&
|
||||
- ((width & (32/bpp-1)) == 0) &&
|
||||
- bpp >= 8 && bpp <= 32)
|
||||
- fast_imageblit(image, p, dst1, fgcolor, bgcolor);
|
||||
- else
|
||||
+ if (!start_index && !pitch_index) {
|
||||
+ if (bpp == 32)
|
||||
+ fast_imageblit32(image, p, dst1, fgcolor,
|
||||
+ bgcolor);
|
||||
+ else if (bpp == 16 && (width & 1) == 0)
|
||||
+ fast_imageblit16(image, p, dst1, fgcolor,
|
||||
+ bgcolor);
|
||||
+ else if (bpp == 8 && (width & 3) == 0)
|
||||
+ fast_imageblit(image, p, dst1, fgcolor,
|
||||
+ bgcolor);
|
||||
+ else
|
||||
+ slow_imageblit(image, p, dst1, fgcolor,
|
||||
+ bgcolor,
|
||||
+ start_index, pitch_index);
|
||||
+ } else
|
||||
slow_imageblit(image, p, dst1, fgcolor, bgcolor,
|
||||
start_index, pitch_index);
|
||||
} else
|
|
@ -1,91 +0,0 @@
|
|||
From d0f5437f8fcabde286fcb89427ef79b93dc4c50d Mon Sep 17 00:00:00 2001
|
||||
From: Siarhei Siamashka <siarhei.siamashka@gmail.com>
|
||||
Date: Mon, 17 Jun 2013 13:32:11 +0300
|
||||
Subject: [PATCH 25/54] fbdev: add FBIOCOPYAREA ioctl
|
||||
|
||||
Based on the patch authored by Ali Gholami Rudi at
|
||||
https://lkml.org/lkml/2009/7/13/153
|
||||
|
||||
Provide an ioctl for userspace applications, but only if this operation
|
||||
is hardware accelerated (otherwide it does not make any sense).
|
||||
|
||||
Signed-off-by: Siarhei Siamashka <siarhei.siamashka@gmail.com>
|
||||
---
|
||||
drivers/video/fbmem.c | 30 ++++++++++++++++++++++++++++++
|
||||
include/uapi/linux/fb.h | 5 +++++
|
||||
2 files changed, 35 insertions(+)
|
||||
|
||||
--- a/drivers/video/fbmem.c
|
||||
+++ b/drivers/video/fbmem.c
|
||||
@@ -1083,6 +1083,25 @@ fb_blank(struct fb_info *info, int blank
|
||||
}
|
||||
EXPORT_SYMBOL(fb_blank);
|
||||
|
||||
+static int fb_copyarea_user(struct fb_info *info,
|
||||
+ struct fb_copyarea *copy)
|
||||
+{
|
||||
+ int ret = 0;
|
||||
+ if (!lock_fb_info(info))
|
||||
+ return -ENODEV;
|
||||
+ if (copy->dx + copy->width > info->var.xres ||
|
||||
+ copy->sx + copy->width > info->var.xres ||
|
||||
+ copy->dy + copy->height > info->var.yres ||
|
||||
+ copy->sy + copy->height > info->var.yres) {
|
||||
+ ret = -EINVAL;
|
||||
+ goto out;
|
||||
+ }
|
||||
+ info->fbops->fb_copyarea(info, copy);
|
||||
+out:
|
||||
+ unlock_fb_info(info);
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
@@ -1093,6 +1112,7 @@ static long do_fb_ioctl(struct fb_info *
|
||||
struct fb_cmap cmap_from;
|
||||
struct fb_cmap_user cmap;
|
||||
struct fb_event event;
|
||||
+ struct fb_copyarea copy;
|
||||
void __user *argp = (void __user *)arg;
|
||||
long ret = 0;
|
||||
|
||||
@@ -1210,6 +1230,15 @@ static long do_fb_ioctl(struct fb_info *
|
||||
unlock_fb_info(info);
|
||||
console_unlock();
|
||||
break;
|
||||
+ case FBIOCOPYAREA:
|
||||
+ if (info->flags & FBINFO_HWACCEL_COPYAREA) {
|
||||
+ /* only provide this ioctl if it is accelerated */
|
||||
+ if (copy_from_user(©, argp, sizeof(copy)))
|
||||
+ return -EFAULT;
|
||||
+ ret = fb_copyarea_user(info, ©);
|
||||
+ break;
|
||||
+ }
|
||||
+ /* fall through */
|
||||
default:
|
||||
if (!lock_fb_info(info))
|
||||
return -ENODEV;
|
||||
@@ -1364,6 +1393,7 @@ static long fb_compat_ioctl(struct file
|
||||
case FBIOPAN_DISPLAY:
|
||||
case FBIOGET_CON2FBMAP:
|
||||
case FBIOPUT_CON2FBMAP:
|
||||
+ case FBIOCOPYAREA:
|
||||
arg = (unsigned long) compat_ptr(arg);
|
||||
case FBIOBLANK:
|
||||
ret = do_fb_ioctl(info, cmd, arg);
|
||||
--- a/include/uapi/linux/fb.h
|
||||
+++ b/include/uapi/linux/fb.h
|
||||
@@ -34,6 +34,11 @@
|
||||
#define FBIOPUT_MODEINFO 0x4617
|
||||
#define FBIOGET_DISPINFO 0x4618
|
||||
#define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32)
|
||||
+/*
|
||||
+ * HACK: use 'z' in order not to clash with any other ioctl numbers which might
|
||||
+ * be concurrently added to the mainline kernel
|
||||
+ */
|
||||
+#define FBIOCOPYAREA _IOW('z', 0x21, struct fb_copyarea)
|
||||
|
||||
#define FB_TYPE_PACKED_PIXELS 0 /* Packed Pixels */
|
||||
#define FB_TYPE_PLANES 1 /* Non interleaved planes */
|
|
@ -1,451 +0,0 @@
|
|||
From c2731f282848af32425043a2df88c1289538983e Mon Sep 17 00:00:00 2001
|
||||
From: Siarhei Siamashka <siarhei.siamashka@gmail.com>
|
||||
Date: Mon, 17 Jun 2013 16:00:25 +0300
|
||||
Subject: [PATCH 26/54] bcm2708_fb: DMA acceleration for fb_copyarea
|
||||
|
||||
Based on http://www.raspberrypi.org/phpBB3/viewtopic.php?p=62425#p62425
|
||||
Also used Simon's dmaer_master module as a reference for tweaking DMA
|
||||
settings for better performance.
|
||||
|
||||
For now busylooping only. IRQ support might be added later.
|
||||
With non-overclocked Raspberry Pi, the performance is ~360 MB/s
|
||||
for simple copy or ~260 MB/s for two-pass copy (used when dragging
|
||||
windows to the right).
|
||||
|
||||
In the case of using DMA channel 0, the performance improves
|
||||
to ~440 MB/s.
|
||||
|
||||
For comparison, VFP optimized CPU copy can only do ~114 MB/s in
|
||||
the same conditions (hindered by reading uncached source buffer).
|
||||
|
||||
Signed-off-by: Siarhei Siamashka <siarhei.siamashka@gmail.com>
|
||||
|
||||
bcm2708_fb: report number of dma copies
|
||||
|
||||
Add a counter (exported via debugfs) reporting the
|
||||
number of dma copies that the framebuffer driver
|
||||
has done, in order to help evaluate different
|
||||
optimization strategies.
|
||||
|
||||
Signed-off-by: Luke Diamand <luked@broadcom.com>
|
||||
|
||||
bcm2708_fb: use IRQ for DMA copies
|
||||
|
||||
The copyarea ioctl() uses DMA to speed things along. This
|
||||
was busy-waiting for completion. This change supports using
|
||||
an interrupt instead for larger transfers. For small
|
||||
transfers, busy-waiting is still likely to be faster.
|
||||
|
||||
Signed-off-by: Luke Diamand <luke@diamand.org>
|
||||
---
|
||||
arch/arm/mach-bcm2708/dma.c | 8 +
|
||||
arch/arm/mach-bcm2708/include/mach/dma.h | 2 +
|
||||
drivers/video/bcm2708_fb.c | 273 ++++++++++++++++++++++++++++++-
|
||||
3 files changed, 278 insertions(+), 5 deletions(-)
|
||||
|
||||
--- a/arch/arm/mach-bcm2708/dma.c
|
||||
+++ b/arch/arm/mach-bcm2708/dma.c
|
||||
@@ -83,6 +83,14 @@ extern void bcm_dma_wait_idle(void __iom
|
||||
|
||||
EXPORT_SYMBOL_GPL(bcm_dma_start);
|
||||
|
||||
+extern bool bcm_dma_is_busy(void __iomem *dma_chan_base)
|
||||
+{
|
||||
+ dsb();
|
||||
+
|
||||
+ return readl(dma_chan_base + BCM2708_DMA_CS) & BCM2708_DMA_ACTIVE;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(bcm_dma_is_busy);
|
||||
+
|
||||
/* Complete an ongoing DMA (assuming its results are to be ignored)
|
||||
Does nothing if there is no DMA in progress.
|
||||
This routine waits for the current AXI transfer to complete before
|
||||
--- a/arch/arm/mach-bcm2708/include/mach/dma.h
|
||||
+++ b/arch/arm/mach-bcm2708/include/mach/dma.h
|
||||
@@ -62,11 +62,13 @@ struct bcm2708_dma_cb {
|
||||
unsigned long next;
|
||||
unsigned long pad[2];
|
||||
};
|
||||
+struct scatterlist;
|
||||
|
||||
extern int bcm_sg_suitable_for_dma(struct scatterlist *sg_ptr, int sg_len);
|
||||
extern void bcm_dma_start(void __iomem *dma_chan_base,
|
||||
dma_addr_t control_block);
|
||||
extern void bcm_dma_wait_idle(void __iomem *dma_chan_base);
|
||||
+extern bool bcm_dma_is_busy(void __iomem *dma_chan_base);
|
||||
extern int /*rc*/ bcm_dma_abort(void __iomem *dma_chan_base);
|
||||
|
||||
/* When listing features we can ask for when allocating DMA channels give
|
||||
--- a/drivers/video/bcm2708_fb.c
|
||||
+++ b/drivers/video/bcm2708_fb.c
|
||||
@@ -21,13 +21,16 @@
|
||||
#include <linux/mm.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/init.h>
|
||||
+#include <linux/interrupt.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/console.h>
|
||||
+#include <linux/debugfs.h>
|
||||
|
||||
+#include <mach/dma.h>
|
||||
#include <mach/platform.h>
|
||||
#include <mach/vcio.h>
|
||||
|
||||
@@ -51,6 +54,10 @@ static int fbheight = 480; /* module par
|
||||
static int fbdepth = 16; /* module parameter */
|
||||
static int fbswap = 0; /* module parameter */
|
||||
|
||||
+static u32 dma_busy_wait_threshold = 1<<15;
|
||||
+module_param(dma_busy_wait_threshold, int, 0644);
|
||||
+MODULE_PARM_DESC(dma_busy_wait_threshold, "Busy-wait for DMA completion below this area");
|
||||
+
|
||||
/* this data structure describes each frame buffer device we find */
|
||||
|
||||
struct fbinfo_s {
|
||||
@@ -62,16 +69,73 @@ struct fbinfo_s {
|
||||
u16 cmap[256];
|
||||
};
|
||||
|
||||
+struct bcm2708_fb_stats {
|
||||
+ struct debugfs_regset32 regset;
|
||||
+ u32 dma_copies;
|
||||
+ u32 dma_irqs;
|
||||
+};
|
||||
+
|
||||
struct bcm2708_fb {
|
||||
struct fb_info fb;
|
||||
struct platform_device *dev;
|
||||
struct fbinfo_s *info;
|
||||
dma_addr_t dma;
|
||||
u32 cmap[16];
|
||||
+ int dma_chan;
|
||||
+ int dma_irq;
|
||||
+ void __iomem *dma_chan_base;
|
||||
+ void *cb_base; /* DMA control blocks */
|
||||
+ dma_addr_t cb_handle;
|
||||
+ struct dentry *debugfs_dir;
|
||||
+ wait_queue_head_t dma_waitq;
|
||||
+ struct bcm2708_fb_stats stats;
|
||||
};
|
||||
|
||||
#define to_bcm2708(info) container_of(info, struct bcm2708_fb, fb)
|
||||
|
||||
+static void bcm2708_fb_debugfs_deinit(struct bcm2708_fb *fb)
|
||||
+{
|
||||
+ debugfs_remove_recursive(fb->debugfs_dir);
|
||||
+ fb->debugfs_dir = NULL;
|
||||
+}
|
||||
+
|
||||
+static int bcm2708_fb_debugfs_init(struct bcm2708_fb *fb)
|
||||
+{
|
||||
+ static struct debugfs_reg32 stats_registers[] = {
|
||||
+ {
|
||||
+ "dma_copies",
|
||||
+ offsetof(struct bcm2708_fb_stats, dma_copies)
|
||||
+ },
|
||||
+ {
|
||||
+ "dma_irqs",
|
||||
+ offsetof(struct bcm2708_fb_stats, dma_irqs)
|
||||
+ },
|
||||
+ };
|
||||
+
|
||||
+ fb->debugfs_dir = debugfs_create_dir(DRIVER_NAME, NULL);
|
||||
+ if (!fb->debugfs_dir) {
|
||||
+ pr_warn("%s: could not create debugfs entry\n",
|
||||
+ __func__);
|
||||
+ return -EFAULT;
|
||||
+ }
|
||||
+
|
||||
+ fb->stats.regset.regs = stats_registers;
|
||||
+ fb->stats.regset.nregs = ARRAY_SIZE(stats_registers);
|
||||
+ fb->stats.regset.base = &fb->stats;
|
||||
+
|
||||
+ if (!debugfs_create_regset32(
|
||||
+ "stats", 0444, fb->debugfs_dir, &fb->stats.regset)) {
|
||||
+ pr_warn("%s: could not create statistics registers\n",
|
||||
+ __func__);
|
||||
+ goto fail;
|
||||
+ }
|
||||
+ return 0;
|
||||
+
|
||||
+fail:
|
||||
+ bcm2708_fb_debugfs_deinit(fb);
|
||||
+ return -EFAULT;
|
||||
+}
|
||||
+
|
||||
static int bcm2708_fb_set_bitfields(struct fb_var_screeninfo *var)
|
||||
{
|
||||
int ret = 0;
|
||||
@@ -322,11 +386,148 @@ static void bcm2708_fb_fillrect(struct f
|
||||
cfb_fillrect(info, rect);
|
||||
}
|
||||
|
||||
+/* A helper function for configuring dma control block */
|
||||
+static void set_dma_cb(struct bcm2708_dma_cb *cb,
|
||||
+ int burst_size,
|
||||
+ dma_addr_t dst,
|
||||
+ int dst_stride,
|
||||
+ dma_addr_t src,
|
||||
+ int src_stride,
|
||||
+ int w,
|
||||
+ int h)
|
||||
+{
|
||||
+ cb->info = BCM2708_DMA_BURST(burst_size) | BCM2708_DMA_S_WIDTH |
|
||||
+ BCM2708_DMA_S_INC | BCM2708_DMA_D_WIDTH |
|
||||
+ BCM2708_DMA_D_INC | BCM2708_DMA_TDMODE;
|
||||
+ cb->dst = dst;
|
||||
+ cb->src = src;
|
||||
+ /*
|
||||
+ * This is not really obvious from the DMA documentation,
|
||||
+ * but the top 16 bits must be programmmed to "height -1"
|
||||
+ * and not "height" in 2D mode.
|
||||
+ */
|
||||
+ cb->length = ((h - 1) << 16) | w;
|
||||
+ cb->stride = ((dst_stride - w) << 16) | (u16)(src_stride - w);
|
||||
+ cb->pad[0] = 0;
|
||||
+ cb->pad[1] = 0;
|
||||
+}
|
||||
+
|
||||
static void bcm2708_fb_copyarea(struct fb_info *info,
|
||||
const struct fb_copyarea *region)
|
||||
{
|
||||
- /*print_debug("bcm2708_fb_copyarea\n"); */
|
||||
- cfb_copyarea(info, region);
|
||||
+ struct bcm2708_fb *fb = to_bcm2708(info);
|
||||
+ struct bcm2708_dma_cb *cb = fb->cb_base;
|
||||
+ int bytes_per_pixel = (info->var.bits_per_pixel + 7) >> 3;
|
||||
+ /* Channel 0 supports larger bursts and is a bit faster */
|
||||
+ int burst_size = (fb->dma_chan == 0) ? 8 : 2;
|
||||
+ int pixels = region->width * region->height;
|
||||
+
|
||||
+ /* Fallback to cfb_copyarea() if we don't like something */
|
||||
+ if (bytes_per_pixel > 4 ||
|
||||
+ info->var.xres * info->var.yres > 1920 * 1200 ||
|
||||
+ region->width <= 0 || region->width > info->var.xres ||
|
||||
+ region->height <= 0 || region->height > info->var.yres ||
|
||||
+ region->sx < 0 || region->sx >= info->var.xres ||
|
||||
+ region->sy < 0 || region->sy >= info->var.yres ||
|
||||
+ region->dx < 0 || region->dx >= info->var.xres ||
|
||||
+ region->dy < 0 || region->dy >= info->var.yres ||
|
||||
+ region->sx + region->width > info->var.xres ||
|
||||
+ region->dx + region->width > info->var.xres ||
|
||||
+ region->sy + region->height > info->var.yres ||
|
||||
+ region->dy + region->height > info->var.yres) {
|
||||
+ cfb_copyarea(info, region);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ if (region->dy == region->sy && region->dx > region->sx) {
|
||||
+ /*
|
||||
+ * A difficult case of overlapped copy. Because DMA can't
|
||||
+ * copy individual scanlines in backwards direction, we need
|
||||
+ * two-pass processing. We do it by programming a chain of dma
|
||||
+ * control blocks in the first 16K part of the buffer and use
|
||||
+ * the remaining 48K as the intermediate temporary scratch
|
||||
+ * buffer. The buffer size is sufficient to handle up to
|
||||
+ * 1920x1200 resolution at 32bpp pixel depth.
|
||||
+ */
|
||||
+ int y;
|
||||
+ dma_addr_t control_block_pa = fb->cb_handle;
|
||||
+ dma_addr_t scratchbuf = fb->cb_handle + 16 * 1024;
|
||||
+ int scanline_size = bytes_per_pixel * region->width;
|
||||
+ int scanlines_per_cb = (64 * 1024 - 16 * 1024) / scanline_size;
|
||||
+
|
||||
+ for (y = 0; y < region->height; y += scanlines_per_cb) {
|
||||
+ dma_addr_t src =
|
||||
+ fb->fb.fix.smem_start +
|
||||
+ bytes_per_pixel * region->sx +
|
||||
+ (region->sy + y) * fb->fb.fix.line_length;
|
||||
+ dma_addr_t dst =
|
||||
+ fb->fb.fix.smem_start +
|
||||
+ bytes_per_pixel * region->dx +
|
||||
+ (region->dy + y) * fb->fb.fix.line_length;
|
||||
+
|
||||
+ if (region->height - y < scanlines_per_cb)
|
||||
+ scanlines_per_cb = region->height - y;
|
||||
+
|
||||
+ set_dma_cb(cb, burst_size, scratchbuf, scanline_size,
|
||||
+ src, fb->fb.fix.line_length,
|
||||
+ scanline_size, scanlines_per_cb);
|
||||
+ control_block_pa += sizeof(struct bcm2708_dma_cb);
|
||||
+ cb->next = control_block_pa;
|
||||
+ cb++;
|
||||
+
|
||||
+ set_dma_cb(cb, burst_size, dst, fb->fb.fix.line_length,
|
||||
+ scratchbuf, scanline_size,
|
||||
+ scanline_size, scanlines_per_cb);
|
||||
+ control_block_pa += sizeof(struct bcm2708_dma_cb);
|
||||
+ cb->next = control_block_pa;
|
||||
+ cb++;
|
||||
+ }
|
||||
+ /* move the pointer back to the last dma control block */
|
||||
+ cb--;
|
||||
+ } else {
|
||||
+ /* A single dma control block is enough. */
|
||||
+ int sy, dy, stride;
|
||||
+ if (region->dy <= region->sy) {
|
||||
+ /* processing from top to bottom */
|
||||
+ dy = region->dy;
|
||||
+ sy = region->sy;
|
||||
+ stride = fb->fb.fix.line_length;
|
||||
+ } else {
|
||||
+ /* processing from bottom to top */
|
||||
+ dy = region->dy + region->height - 1;
|
||||
+ sy = region->sy + region->height - 1;
|
||||
+ stride = -fb->fb.fix.line_length;
|
||||
+ }
|
||||
+ set_dma_cb(cb, burst_size,
|
||||
+ fb->fb.fix.smem_start + dy * fb->fb.fix.line_length +
|
||||
+ bytes_per_pixel * region->dx,
|
||||
+ stride,
|
||||
+ fb->fb.fix.smem_start + sy * fb->fb.fix.line_length +
|
||||
+ bytes_per_pixel * region->sx,
|
||||
+ stride,
|
||||
+ region->width * bytes_per_pixel,
|
||||
+ region->height);
|
||||
+ }
|
||||
+
|
||||
+ /* end of dma control blocks chain */
|
||||
+ cb->next = 0;
|
||||
+
|
||||
+
|
||||
+ if (pixels < dma_busy_wait_threshold) {
|
||||
+ bcm_dma_start(fb->dma_chan_base, fb->cb_handle);
|
||||
+ bcm_dma_wait_idle(fb->dma_chan_base);
|
||||
+ } else {
|
||||
+ void __iomem *dma_chan = fb->dma_chan_base;
|
||||
+ cb->info |= BCM2708_DMA_INT_EN;
|
||||
+ bcm_dma_start(fb->dma_chan_base, fb->cb_handle);
|
||||
+ while (bcm_dma_is_busy(dma_chan)) {
|
||||
+ wait_event_interruptible(
|
||||
+ fb->dma_waitq,
|
||||
+ !bcm_dma_is_busy(dma_chan));
|
||||
+ }
|
||||
+ fb->stats.dma_irqs++;
|
||||
+ }
|
||||
+ fb->stats.dma_copies++;
|
||||
}
|
||||
|
||||
static void bcm2708_fb_imageblit(struct fb_info *info,
|
||||
@@ -336,6 +537,24 @@ static void bcm2708_fb_imageblit(struct
|
||||
cfb_imageblit(info, image);
|
||||
}
|
||||
|
||||
+static irqreturn_t bcm2708_fb_dma_irq(int irq, void *cxt)
|
||||
+{
|
||||
+ struct bcm2708_fb *fb = cxt;
|
||||
+
|
||||
+ /* FIXME: should read status register to check if this is
|
||||
+ * actually interrupting us or not, in case this interrupt
|
||||
+ * ever becomes shared amongst several DMA channels
|
||||
+ *
|
||||
+ * readl(dma_chan_base + BCM2708_DMA_CS) & BCM2708_DMA_IRQ;
|
||||
+ */
|
||||
+
|
||||
+ /* acknowledge the interrupt */
|
||||
+ writel(BCM2708_DMA_INT, fb->dma_chan_base + BCM2708_DMA_CS);
|
||||
+
|
||||
+ wake_up(&fb->dma_waitq);
|
||||
+ return IRQ_HANDLED;
|
||||
+}
|
||||
+
|
||||
static struct fb_ops bcm2708_fb_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.fb_check_var = bcm2708_fb_check_var,
|
||||
@@ -365,7 +584,7 @@ static int bcm2708_fb_register(struct bc
|
||||
fb->dma = dma;
|
||||
}
|
||||
fb->fb.fbops = &bcm2708_fb_ops;
|
||||
- fb->fb.flags = FBINFO_FLAG_DEFAULT;
|
||||
+ fb->fb.flags = FBINFO_FLAG_DEFAULT | FBINFO_HWACCEL_COPYAREA;
|
||||
fb->fb.pseudo_palette = fb->cmap;
|
||||
|
||||
strncpy(fb->fb.fix.id, bcm2708_name, sizeof(fb->fb.fix.id));
|
||||
@@ -396,6 +615,7 @@ static int bcm2708_fb_register(struct bc
|
||||
fb->fb.monspecs.dclkmax = 100000000;
|
||||
|
||||
bcm2708_fb_set_bitfields(&fb->fb.var);
|
||||
+ init_waitqueue_head(&fb->dma_waitq);
|
||||
|
||||
/*
|
||||
* Allocate colourmap.
|
||||
@@ -421,14 +641,45 @@ static int bcm2708_fb_probe(struct platf
|
||||
struct bcm2708_fb *fb;
|
||||
int ret;
|
||||
|
||||
- fb = kmalloc(sizeof(struct bcm2708_fb), GFP_KERNEL);
|
||||
+ fb = kzalloc(sizeof(struct bcm2708_fb), GFP_KERNEL);
|
||||
if (!fb) {
|
||||
dev_err(&dev->dev,
|
||||
"could not allocate new bcm2708_fb struct\n");
|
||||
ret = -ENOMEM;
|
||||
goto free_region;
|
||||
}
|
||||
- memset(fb, 0, sizeof(struct bcm2708_fb));
|
||||
+
|
||||
+ bcm2708_fb_debugfs_init(fb);
|
||||
+
|
||||
+ fb->cb_base = dma_alloc_writecombine(&dev->dev, SZ_64K,
|
||||
+ &fb->cb_handle, GFP_KERNEL);
|
||||
+ if (!fb->cb_base) {
|
||||
+ dev_err(&dev->dev, "cannot allocate DMA CBs\n");
|
||||
+ ret = -ENOMEM;
|
||||
+ goto free_fb;
|
||||
+ }
|
||||
+
|
||||
+ pr_info("BCM2708FB: allocated DMA memory %08x\n",
|
||||
+ fb->cb_handle);
|
||||
+
|
||||
+ ret = bcm_dma_chan_alloc(BCM_DMA_FEATURE_BULK,
|
||||
+ &fb->dma_chan_base, &fb->dma_irq);
|
||||
+ if (ret < 0) {
|
||||
+ dev_err(&dev->dev, "couldn't allocate a DMA channel\n");
|
||||
+ goto free_cb;
|
||||
+ }
|
||||
+ fb->dma_chan = ret;
|
||||
+
|
||||
+ ret = request_irq(fb->dma_irq, bcm2708_fb_dma_irq,
|
||||
+ 0, "bcm2708_fb dma", fb);
|
||||
+ if (ret) {
|
||||
+ pr_err("%s: failed to request DMA irq\n", __func__);
|
||||
+ goto free_dma_chan;
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ pr_info("BCM2708FB: allocated DMA channel %d @ %p\n",
|
||||
+ fb->dma_chan, fb->dma_chan_base);
|
||||
|
||||
fb->dev = dev;
|
||||
|
||||
@@ -438,6 +689,11 @@ static int bcm2708_fb_probe(struct platf
|
||||
goto out;
|
||||
}
|
||||
|
||||
+free_dma_chan:
|
||||
+ bcm_dma_chan_free(fb->dma_chan);
|
||||
+free_cb:
|
||||
+ dma_free_writecombine(&dev->dev, SZ_64K, fb->cb_base, fb->cb_handle);
|
||||
+free_fb:
|
||||
kfree(fb);
|
||||
free_region:
|
||||
dev_err(&dev->dev, "probe failed, err %d\n", ret);
|
||||
@@ -455,8 +711,15 @@ static int bcm2708_fb_remove(struct plat
|
||||
iounmap(fb->fb.screen_base);
|
||||
unregister_framebuffer(&fb->fb);
|
||||
|
||||
+ dma_free_writecombine(&dev->dev, SZ_64K, fb->cb_base, fb->cb_handle);
|
||||
+ bcm_dma_chan_free(fb->dma_chan);
|
||||
+
|
||||
dma_free_coherent(NULL, PAGE_ALIGN(sizeof(*fb->info)), (void *)fb->info,
|
||||
fb->dma);
|
||||
+ bcm2708_fb_debugfs_deinit(fb);
|
||||
+
|
||||
+ free_irq(fb->dma_irq, fb);
|
||||
+
|
||||
kfree(fb);
|
||||
|
||||
return 0;
|
|
@ -1,68 +0,0 @@
|
|||
From 6f2dc468edc31cf2c9b2098566cbbcc1e59b5ad5 Mon Sep 17 00:00:00 2001
|
||||
From: popcornmix <popcornmix@gmail.com>
|
||||
Date: Wed, 18 Dec 2013 22:16:19 +0000
|
||||
Subject: [PATCH 27/54] config: Enable CONFIG_MEMCG, but leave it disabled (due
|
||||
to memory cost). Enable with cgroup_enable=memory.
|
||||
|
||||
---
|
||||
arch/arm/configs/bcmrpi_defconfig | 1 +
|
||||
kernel/cgroup.c | 27 +++++++++++++++++++++++++++
|
||||
mm/memcontrol.c | 1 +
|
||||
3 files changed, 29 insertions(+)
|
||||
|
||||
--- a/arch/arm/configs/bcmrpi_defconfig
|
||||
+++ b/arch/arm/configs/bcmrpi_defconfig
|
||||
@@ -18,6 +18,7 @@ CONFIG_CGROUP_FREEZER=y
|
||||
CONFIG_CGROUP_DEVICE=y
|
||||
CONFIG_CGROUP_CPUACCT=y
|
||||
CONFIG_RESOURCE_COUNTERS=y
|
||||
+CONFIG_MEMCG=y
|
||||
CONFIG_BLK_CGROUP=y
|
||||
CONFIG_NAMESPACES=y
|
||||
CONFIG_SCHED_AUTOGROUP=y
|
||||
--- a/kernel/cgroup.c
|
||||
+++ b/kernel/cgroup.c
|
||||
@@ -5251,6 +5251,33 @@ static int __init cgroup_disable(char *s
|
||||
}
|
||||
__setup("cgroup_disable=", cgroup_disable);
|
||||
|
||||
+static int __init cgroup_enable(char *str)
|
||||
+{
|
||||
+ struct cgroup_subsys *ss;
|
||||
+ char *token;
|
||||
+ int i;
|
||||
+
|
||||
+ while ((token = strsep(&str, ",")) != NULL) {
|
||||
+ if (!*token)
|
||||
+ continue;
|
||||
+
|
||||
+ /*
|
||||
+ * cgroup_disable, being at boot time, can't know about
|
||||
+ * module subsystems, so we don't worry about them.
|
||||
+ */
|
||||
+ for_each_builtin_subsys(ss, i) {
|
||||
+ if (!strcmp(token, ss->name)) {
|
||||
+ ss->disabled = 0;
|
||||
+ printk(KERN_INFO "Disabling %s control group"
|
||||
+ " subsystem\n", ss->name);
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ return 1;
|
||||
+}
|
||||
+__setup("cgroup_enable=", cgroup_enable);
|
||||
+
|
||||
/**
|
||||
* css_from_dir - get corresponding css from the dentry of a cgroup dir
|
||||
* @dentry: directory dentry of interest
|
||||
--- a/mm/memcontrol.c
|
||||
+++ b/mm/memcontrol.c
|
||||
@@ -7316,6 +7316,7 @@ struct cgroup_subsys mem_cgroup_subsys =
|
||||
.bind = mem_cgroup_bind,
|
||||
.base_cftypes = mem_cgroup_files,
|
||||
.early_init = 0,
|
||||
+ .disabled = 1,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_MEMCG_SWAP
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,462 +0,0 @@
|
|||
From 2172c6578ef13acb8fcf5cac643cb1ee8e824117 Mon Sep 17 00:00:00 2001
|
||||
From: Julian Scheel <julian@jusst.de>
|
||||
Date: Wed, 19 Feb 2014 16:06:59 +0100
|
||||
Subject: [PATCH 32/54] snd-bcm2835: Add support for spdif/hdmi passthrough
|
||||
|
||||
This adds a dedicated subdevice which can be used for passthrough of non-audio
|
||||
formats (ie encoded a52) through the hdmi audio link. In addition to this
|
||||
driver extension an appropriate card config is required to make alsa-lib
|
||||
support the AES parameters for this device.
|
||||
---
|
||||
sound/arm/bcm2835-ctl.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++
|
||||
sound/arm/bcm2835-pcm.c | 128 +++++++++++++++++++++++++++++++++++++++++-------
|
||||
sound/arm/bcm2835.c | 9 +++-
|
||||
sound/arm/bcm2835.h | 9 ++++
|
||||
4 files changed, 250 insertions(+), 19 deletions(-)
|
||||
|
||||
--- a/sound/arm/bcm2835-ctl.c
|
||||
+++ b/sound/arm/bcm2835-ctl.c
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <sound/rawmidi.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/tlv.h>
|
||||
+#include <sound/asoundef.h>
|
||||
|
||||
#include "bcm2835.h"
|
||||
|
||||
@@ -183,6 +184,122 @@ static struct snd_kcontrol_new snd_bcm28
|
||||
},
|
||||
};
|
||||
|
||||
+static int snd_bcm2835_spdif_default_info(struct snd_kcontrol *kcontrol,
|
||||
+ struct snd_ctl_elem_info *uinfo)
|
||||
+{
|
||||
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
|
||||
+ uinfo->count = 1;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int snd_bcm2835_spdif_default_get(struct snd_kcontrol *kcontrol,
|
||||
+ struct snd_ctl_elem_value *ucontrol)
|
||||
+{
|
||||
+ struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
|
||||
+ int i;
|
||||
+
|
||||
+ for (i = 0; i < 4; i++)
|
||||
+ ucontrol->value.iec958.status[i] =
|
||||
+ (chip->spdif_status >> (i * 8)) && 0xff;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int snd_bcm2835_spdif_default_put(struct snd_kcontrol *kcontrol,
|
||||
+ struct snd_ctl_elem_value *ucontrol)
|
||||
+{
|
||||
+ struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
|
||||
+ unsigned int val = 0;
|
||||
+ int i, change;
|
||||
+
|
||||
+ for (i = 0; i < 4; i++)
|
||||
+ val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8);
|
||||
+
|
||||
+ change = val != chip->spdif_status;
|
||||
+ chip->spdif_status = val;
|
||||
+
|
||||
+ return change;
|
||||
+}
|
||||
+
|
||||
+static int snd_bcm2835_spdif_mask_info(struct snd_kcontrol *kcontrol,
|
||||
+ struct snd_ctl_elem_info *uinfo)
|
||||
+{
|
||||
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
|
||||
+ uinfo->count = 1;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int snd_bcm2835_spdif_mask_get(struct snd_kcontrol *kcontrol,
|
||||
+ struct snd_ctl_elem_value *ucontrol)
|
||||
+{
|
||||
+ /* bcm2835 supports only consumer mode and sets all other format flags
|
||||
+ * automatically. So the only thing left is signalling non-audio
|
||||
+ * content */
|
||||
+ ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int snd_bcm2835_spdif_stream_info(struct snd_kcontrol *kcontrol,
|
||||
+ struct snd_ctl_elem_info *uinfo)
|
||||
+{
|
||||
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
|
||||
+ uinfo->count = 1;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int snd_bcm2835_spdif_stream_get(struct snd_kcontrol *kcontrol,
|
||||
+ struct snd_ctl_elem_value *ucontrol)
|
||||
+{
|
||||
+ struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
|
||||
+ int i;
|
||||
+
|
||||
+ for (i = 0; i < 4; i++)
|
||||
+ ucontrol->value.iec958.status[i] =
|
||||
+ (chip->spdif_status >> (i * 8)) & 0xff;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int snd_bcm2835_spdif_stream_put(struct snd_kcontrol *kcontrol,
|
||||
+ struct snd_ctl_elem_value *ucontrol)
|
||||
+{
|
||||
+ struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
|
||||
+ unsigned int val = 0;
|
||||
+ int i, change;
|
||||
+
|
||||
+ for (i = 0; i < 4; i++)
|
||||
+ val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8);
|
||||
+ change = val != chip->spdif_status;
|
||||
+ chip->spdif_status = val;
|
||||
+
|
||||
+ return change;
|
||||
+}
|
||||
+
|
||||
+static struct snd_kcontrol_new snd_bcm2835_spdif[] = {
|
||||
+ {
|
||||
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
|
||||
+ .info = snd_bcm2835_spdif_default_info,
|
||||
+ .get = snd_bcm2835_spdif_default_get,
|
||||
+ .put = snd_bcm2835_spdif_default_put
|
||||
+ },
|
||||
+ {
|
||||
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
|
||||
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK),
|
||||
+ .info = snd_bcm2835_spdif_mask_info,
|
||||
+ .get = snd_bcm2835_spdif_mask_get,
|
||||
+ },
|
||||
+ {
|
||||
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
|
||||
+ SNDRV_CTL_ELEM_ACCESS_INACTIVE,
|
||||
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM),
|
||||
+ .info = snd_bcm2835_spdif_stream_info,
|
||||
+ .get = snd_bcm2835_spdif_stream_get,
|
||||
+ .put = snd_bcm2835_spdif_stream_put,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
int snd_bcm2835_new_ctl(bcm2835_chip_t * chip)
|
||||
{
|
||||
int err;
|
||||
@@ -196,5 +313,11 @@ int snd_bcm2835_new_ctl(bcm2835_chip_t *
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
+ for (idx = 0; idx < ARRAY_SIZE(snd_bcm2835_spdif); idx++) {
|
||||
+ err = snd_ctl_add(chip->card,
|
||||
+ snd_ctl_new1(&snd_bcm2835_spdif[idx], chip));
|
||||
+ if (err < 0)
|
||||
+ return err;
|
||||
+ }
|
||||
return 0;
|
||||
}
|
||||
--- a/sound/arm/bcm2835-pcm.c
|
||||
+++ b/sound/arm/bcm2835-pcm.c
|
||||
@@ -15,6 +15,8 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
+#include <sound/asoundef.h>
|
||||
+
|
||||
#include "bcm2835.h"
|
||||
|
||||
/* hardware definition */
|
||||
@@ -34,6 +36,23 @@ static struct snd_pcm_hardware snd_bcm28
|
||||
.periods_max = 128,
|
||||
};
|
||||
|
||||
+static struct snd_pcm_hardware snd_bcm2835_playback_spdif_hw = {
|
||||
+ .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
|
||||
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_44100 |
|
||||
+ SNDRV_PCM_RATE_48000,
|
||||
+ .rate_min = 44100,
|
||||
+ .rate_max = 48000,
|
||||
+ .channels_min = 2,
|
||||
+ .channels_max = 2,
|
||||
+ .buffer_bytes_max = 128 * 1024,
|
||||
+ .period_bytes_min = 1 * 1024,
|
||||
+ .period_bytes_max = 128 * 1024,
|
||||
+ .periods_min = 1,
|
||||
+ .periods_max = 128,
|
||||
+};
|
||||
+
|
||||
static void snd_bcm2835_playback_free(struct snd_pcm_runtime *runtime)
|
||||
{
|
||||
audio_info("Freeing up alsa stream here ..\n");
|
||||
@@ -89,7 +108,8 @@ static irqreturn_t bcm2835_playback_fifo
|
||||
}
|
||||
|
||||
/* open callback */
|
||||
-static int snd_bcm2835_playback_open(struct snd_pcm_substream *substream)
|
||||
+static int snd_bcm2835_playback_open_generic(
|
||||
+ struct snd_pcm_substream *substream, int spdif)
|
||||
{
|
||||
bcm2835_chip_t *chip = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
@@ -102,6 +122,11 @@ static int snd_bcm2835_playback_open(str
|
||||
audio_info("Alsa open (%d)\n", substream->number);
|
||||
idx = substream->number;
|
||||
|
||||
+ if (spdif && chip->opened != 0)
|
||||
+ return -EBUSY;
|
||||
+ else if (!spdif && (chip->opened & (1 << idx)))
|
||||
+ return -EBUSY;
|
||||
+
|
||||
if (idx > MAX_SUBSTREAMS) {
|
||||
audio_error
|
||||
("substream(%d) device doesn't exist max(%d) substreams allowed\n",
|
||||
@@ -143,13 +168,20 @@ static int snd_bcm2835_playback_open(str
|
||||
}
|
||||
runtime->private_data = alsa_stream;
|
||||
runtime->private_free = snd_bcm2835_playback_free;
|
||||
- runtime->hw = snd_bcm2835_playback_hw;
|
||||
+ if (spdif) {
|
||||
+ runtime->hw = snd_bcm2835_playback_spdif_hw;
|
||||
+ } else {
|
||||
+ /* clear spdif status, as we are not in spdif mode */
|
||||
+ chip->spdif_status = 0;
|
||||
+ runtime->hw = snd_bcm2835_playback_hw;
|
||||
+ }
|
||||
/* minimum 16 bytes alignment (for vchiq bulk transfers) */
|
||||
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
|
||||
16);
|
||||
|
||||
chip->alsa_stream[idx] = alsa_stream;
|
||||
|
||||
+ chip->opened |= (1 << idx);
|
||||
alsa_stream->open = 1;
|
||||
alsa_stream->draining = 1;
|
||||
|
||||
@@ -159,6 +191,16 @@ out:
|
||||
return err;
|
||||
}
|
||||
|
||||
+static int snd_bcm2835_playback_open(struct snd_pcm_substream *substream)
|
||||
+{
|
||||
+ return snd_bcm2835_playback_open_generic(substream, 0);
|
||||
+}
|
||||
+
|
||||
+static int snd_bcm2835_playback_spdif_open(struct snd_pcm_substream *substream)
|
||||
+{
|
||||
+ return snd_bcm2835_playback_open_generic(substream, 1);
|
||||
+}
|
||||
+
|
||||
/* close callback */
|
||||
static int snd_bcm2835_playback_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
@@ -166,6 +208,7 @@ static int snd_bcm2835_playback_close(st
|
||||
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
bcm2835_alsa_stream_t *alsa_stream = runtime->private_data;
|
||||
+ bcm2835_chip_t *chip = snd_pcm_substream_chip(substream);
|
||||
|
||||
audio_info(" .. IN\n");
|
||||
audio_info("Alsa close\n");
|
||||
@@ -196,6 +239,8 @@ static int snd_bcm2835_playback_close(st
|
||||
* runtime->private_free callback we registered in *_open above
|
||||
*/
|
||||
|
||||
+ chip->opened &= ~(1 << substream->number);
|
||||
+
|
||||
audio_info(" .. OUT\n");
|
||||
|
||||
return 0;
|
||||
@@ -205,10 +250,9 @@ static int snd_bcm2835_playback_close(st
|
||||
static int snd_bcm2835_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
- int err;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
- bcm2835_alsa_stream_t *alsa_stream =
|
||||
- (bcm2835_alsa_stream_t *) runtime->private_data;
|
||||
+ bcm2835_alsa_stream_t *alsa_stream = runtime->private_data;
|
||||
+ int err;
|
||||
|
||||
audio_info(" .. IN\n");
|
||||
|
||||
@@ -219,19 +263,9 @@ static int snd_bcm2835_pcm_hw_params(str
|
||||
return err;
|
||||
}
|
||||
|
||||
- err = bcm2835_audio_set_params(alsa_stream, params_channels(params),
|
||||
- params_rate(params),
|
||||
- snd_pcm_format_width(params_format
|
||||
- (params)));
|
||||
- if (err < 0) {
|
||||
- audio_error(" error setting hw params\n");
|
||||
- }
|
||||
-
|
||||
- bcm2835_audio_setup(alsa_stream);
|
||||
-
|
||||
- /* in preparation of the stream, set the controls (volume level) of the stream */
|
||||
- bcm2835_audio_set_ctls(alsa_stream->chip);
|
||||
-
|
||||
+ alsa_stream->channels = params_channels(params);
|
||||
+ alsa_stream->params_rate = params_rate(params);
|
||||
+ alsa_stream->pcm_format_width = snd_pcm_format_width(params_format (params));
|
||||
audio_info(" .. OUT\n");
|
||||
|
||||
return err;
|
||||
@@ -247,11 +281,35 @@ static int snd_bcm2835_pcm_hw_free(struc
|
||||
/* prepare callback */
|
||||
static int snd_bcm2835_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
+ bcm2835_chip_t *chip = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
bcm2835_alsa_stream_t *alsa_stream = runtime->private_data;
|
||||
+ int channels;
|
||||
+ int err;
|
||||
|
||||
audio_info(" .. IN\n");
|
||||
|
||||
+ /* notify the vchiq that it should enter spdif passthrough mode by
|
||||
+ * setting channels=0 (see
|
||||
+ * https://github.com/raspberrypi/linux/issues/528) */
|
||||
+ if (chip->spdif_status & IEC958_AES0_NONAUDIO)
|
||||
+ channels = 0;
|
||||
+ else
|
||||
+ channels = alsa_stream->channels;
|
||||
+
|
||||
+ err = bcm2835_audio_set_params(alsa_stream, channels,
|
||||
+ alsa_stream->params_rate,
|
||||
+ alsa_stream->pcm_format_width);
|
||||
+ if (err < 0) {
|
||||
+ audio_error(" error setting hw params\n");
|
||||
+ }
|
||||
+
|
||||
+ bcm2835_audio_setup(alsa_stream);
|
||||
+
|
||||
+ /* in preparation of the stream, set the controls (volume level) of the stream */
|
||||
+ bcm2835_audio_set_ctls(alsa_stream->chip);
|
||||
+
|
||||
+
|
||||
memset(&alsa_stream->pcm_indirect, 0, sizeof(alsa_stream->pcm_indirect));
|
||||
|
||||
alsa_stream->pcm_indirect.hw_buffer_size =
|
||||
@@ -392,6 +450,18 @@ static struct snd_pcm_ops snd_bcm2835_pl
|
||||
.ack = snd_bcm2835_pcm_ack,
|
||||
};
|
||||
|
||||
+static struct snd_pcm_ops snd_bcm2835_playback_spdif_ops = {
|
||||
+ .open = snd_bcm2835_playback_spdif_open,
|
||||
+ .close = snd_bcm2835_playback_close,
|
||||
+ .ioctl = snd_bcm2835_pcm_lib_ioctl,
|
||||
+ .hw_params = snd_bcm2835_pcm_hw_params,
|
||||
+ .hw_free = snd_bcm2835_pcm_hw_free,
|
||||
+ .prepare = snd_bcm2835_pcm_prepare,
|
||||
+ .trigger = snd_bcm2835_pcm_trigger,
|
||||
+ .pointer = snd_bcm2835_pcm_pointer,
|
||||
+ .ack = snd_bcm2835_pcm_ack,
|
||||
+};
|
||||
+
|
||||
/* create a pcm device */
|
||||
int snd_bcm2835_new_pcm(bcm2835_chip_t * chip)
|
||||
{
|
||||
@@ -424,3 +494,25 @@ int snd_bcm2835_new_pcm(bcm2835_chip_t *
|
||||
|
||||
return 0;
|
||||
}
|
||||
+
|
||||
+int snd_bcm2835_new_spdif_pcm(bcm2835_chip_t * chip)
|
||||
+{
|
||||
+ struct snd_pcm *pcm;
|
||||
+ int err;
|
||||
+
|
||||
+ err = snd_pcm_new(chip->card, "bcm2835 ALSA", 1, 1, 0, &pcm);
|
||||
+ if (err < 0)
|
||||
+ return err;
|
||||
+
|
||||
+ pcm->private_data = chip;
|
||||
+ strcpy(pcm->name, "bcm2835 IEC958/HDMI");
|
||||
+ chip->pcm_spdif = pcm;
|
||||
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
|
||||
+ &snd_bcm2835_playback_spdif_ops);
|
||||
+
|
||||
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
|
||||
+ snd_dma_continuous_data (GFP_KERNEL),
|
||||
+ 64 * 1024, 64 * 1024);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
--- a/sound/arm/bcm2835.c
|
||||
+++ b/sound/arm/bcm2835.c
|
||||
@@ -104,7 +104,7 @@ static int snd_bcm2835_alsa_probe(struct
|
||||
goto out;
|
||||
|
||||
snd_card_set_dev(g_card, &pdev->dev);
|
||||
- strcpy(g_card->driver, "BRCM bcm2835 ALSA Driver");
|
||||
+ strcpy(g_card->driver, "bcm2835");
|
||||
strcpy(g_card->shortname, "bcm2835 ALSA");
|
||||
sprintf(g_card->longname, "%s", g_card->shortname);
|
||||
|
||||
@@ -121,6 +121,12 @@ static int snd_bcm2835_alsa_probe(struct
|
||||
goto out_bcm2835_new_pcm;
|
||||
}
|
||||
|
||||
+ err = snd_bcm2835_new_spdif_pcm(chip);
|
||||
+ if (err < 0) {
|
||||
+ dev_err(&pdev->dev, "Failed to create new BCM2835 spdif pcm device\n");
|
||||
+ goto out_bcm2835_new_spdif;
|
||||
+ }
|
||||
+
|
||||
err = snd_bcm2835_new_ctl(chip);
|
||||
if (err < 0) {
|
||||
dev_err(&pdev->dev, "Failed to create new BCM2835 ctl\n");
|
||||
@@ -156,6 +162,7 @@ add_register_map:
|
||||
|
||||
out_card_register:
|
||||
out_bcm2835_new_ctl:
|
||||
+out_bcm2835_new_spdif:
|
||||
out_bcm2835_new_pcm:
|
||||
out_bcm2835_create:
|
||||
BUG_ON(!g_card);
|
||||
--- a/sound/arm/bcm2835.h
|
||||
+++ b/sound/arm/bcm2835.h
|
||||
@@ -97,6 +97,7 @@ typedef enum {
|
||||
typedef struct bcm2835_chip {
|
||||
struct snd_card *card;
|
||||
struct snd_pcm *pcm;
|
||||
+ struct snd_pcm *pcm_spdif;
|
||||
/* Bitmat for valid reg_base and irq numbers */
|
||||
uint32_t avail_substreams;
|
||||
struct platform_device *pdev[MAX_SUBSTREAMS];
|
||||
@@ -106,6 +107,9 @@ typedef struct bcm2835_chip {
|
||||
int old_volume; /* stores the volume value whist muted */
|
||||
int dest;
|
||||
int mute;
|
||||
+
|
||||
+ unsigned int opened;
|
||||
+ unsigned int spdif_status;
|
||||
} bcm2835_chip_t;
|
||||
|
||||
typedef struct bcm2835_alsa_stream {
|
||||
@@ -123,6 +127,10 @@ typedef struct bcm2835_alsa_stream {
|
||||
int running;
|
||||
int draining;
|
||||
|
||||
+ int channels;
|
||||
+ int params_rate;
|
||||
+ int pcm_format_width;
|
||||
+
|
||||
unsigned int pos;
|
||||
unsigned int buffer_size;
|
||||
unsigned int period_size;
|
||||
@@ -138,6 +146,7 @@ typedef struct bcm2835_alsa_stream {
|
||||
|
||||
int snd_bcm2835_new_ctl(bcm2835_chip_t * chip);
|
||||
int snd_bcm2835_new_pcm(bcm2835_chip_t * chip);
|
||||
+int snd_bcm2835_new_spdif_pcm(bcm2835_chip_t * chip);
|
||||
|
||||
int bcm2835_audio_open(bcm2835_alsa_stream_t * alsa_stream);
|
||||
int bcm2835_audio_close(bcm2835_alsa_stream_t * alsa_stream);
|
|
@ -1,632 +0,0 @@
|
|||
From 568b7292b8e7e1fe3d852db8b463d989d06b5adf Mon Sep 17 00:00:00 2001
|
||||
From: Florian Meier <florian.meier@koalo.de>
|
||||
Date: Fri, 22 Nov 2013 14:22:53 +0100
|
||||
Subject: [PATCH 33/54] dmaengine: Add support for BCM2708
|
||||
|
||||
Add support for DMA controller of BCM2708 as used in the Raspberry Pi.
|
||||
Currently it only supports cyclic DMA.
|
||||
|
||||
Signed-off-by: Florian Meier <florian.meier@koalo.de>
|
||||
---
|
||||
drivers/dma/Kconfig | 6 +
|
||||
drivers/dma/Makefile | 1 +
|
||||
drivers/dma/bcm2708-dmaengine.c | 588 ++++++++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 595 insertions(+)
|
||||
create mode 100644 drivers/dma/bcm2708-dmaengine.c
|
||||
|
||||
--- a/drivers/dma/Kconfig
|
||||
+++ b/drivers/dma/Kconfig
|
||||
@@ -312,6 +312,12 @@ config DMA_BCM2835
|
||||
select DMA_ENGINE
|
||||
select DMA_VIRTUAL_CHANNELS
|
||||
|
||||
+config DMA_BCM2708
|
||||
+ tristate "BCM2708 DMA engine support"
|
||||
+ depends on MACH_BCM2708
|
||||
+ select DMA_ENGINE
|
||||
+ select DMA_VIRTUAL_CHANNELS
|
||||
+
|
||||
config TI_CPPI41
|
||||
tristate "AM33xx CPPI41 DMA support"
|
||||
depends on ARCH_OMAP
|
||||
--- a/drivers/dma/Makefile
|
||||
+++ b/drivers/dma/Makefile
|
||||
@@ -39,6 +39,7 @@ obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o
|
||||
obj-$(CONFIG_MMP_TDMA) += mmp_tdma.o
|
||||
obj-$(CONFIG_DMA_OMAP) += omap-dma.o
|
||||
obj-$(CONFIG_DMA_BCM2835) += bcm2835-dma.o
|
||||
+obj-$(CONFIG_DMA_BCM2708) += bcm2708-dmaengine.o
|
||||
obj-$(CONFIG_MMP_PDMA) += mmp_pdma.o
|
||||
obj-$(CONFIG_DMA_JZ4740) += dma-jz4740.o
|
||||
obj-$(CONFIG_TI_CPPI41) += cppi41.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/dma/bcm2708-dmaengine.c
|
||||
@@ -0,0 +1,588 @@
|
||||
+/*
|
||||
+ * BCM2708 DMA engine support
|
||||
+ *
|
||||
+ * This driver only supports cyclic DMA transfers
|
||||
+ * as needed for the I2S module.
|
||||
+ *
|
||||
+ * Author: Florian Meier <florian.meier@koalo.de>
|
||||
+ * Copyright 2013
|
||||
+ *
|
||||
+ * Based on
|
||||
+ * OMAP DMAengine support by Russell King
|
||||
+ *
|
||||
+ * BCM2708 DMA Driver
|
||||
+ * Copyright (C) 2010 Broadcom
|
||||
+ *
|
||||
+ * Raspberry Pi PCM I2S ALSA Driver
|
||||
+ * Copyright (c) by Phil Poole 2013
|
||||
+ *
|
||||
+ * MARVELL MMP Peripheral DMA Driver
|
||||
+ * Copyright 2012 Marvell International Ltd.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License as published by
|
||||
+ * the Free Software Foundation; either version 2 of the License, or
|
||||
+ * (at your option) any later version.
|
||||
+ *
|
||||
+ * 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/dmaengine.h>
|
||||
+#include <linux/dma-mapping.h>
|
||||
+#include <linux/err.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/interrupt.h>
|
||||
+#include <linux/list.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/spinlock.h>
|
||||
+#include <linux/irq.h>
|
||||
+
|
||||
+#include "virt-dma.h"
|
||||
+
|
||||
+#include <mach/dma.h>
|
||||
+#include <mach/irqs.h>
|
||||
+
|
||||
+struct bcm2708_dmadev {
|
||||
+ struct dma_device ddev;
|
||||
+ spinlock_t lock;
|
||||
+ void __iomem *base;
|
||||
+ struct device_dma_parameters dma_parms;
|
||||
+};
|
||||
+
|
||||
+struct bcm2708_chan {
|
||||
+ struct virt_dma_chan vc;
|
||||
+ struct list_head node;
|
||||
+
|
||||
+ struct dma_slave_config cfg;
|
||||
+ bool cyclic;
|
||||
+
|
||||
+ int ch;
|
||||
+ struct bcm2708_desc *desc;
|
||||
+
|
||||
+ void __iomem *chan_base;
|
||||
+ int irq_number;
|
||||
+};
|
||||
+
|
||||
+struct bcm2708_desc {
|
||||
+ struct virt_dma_desc vd;
|
||||
+ enum dma_transfer_direction dir;
|
||||
+
|
||||
+ unsigned int control_block_size;
|
||||
+ struct bcm2708_dma_cb *control_block_base;
|
||||
+ dma_addr_t control_block_base_phys;
|
||||
+
|
||||
+ unsigned frames;
|
||||
+ size_t size;
|
||||
+};
|
||||
+
|
||||
+#define BCM2708_DMA_DATA_TYPE_S8 1
|
||||
+#define BCM2708_DMA_DATA_TYPE_S16 2
|
||||
+#define BCM2708_DMA_DATA_TYPE_S32 4
|
||||
+#define BCM2708_DMA_DATA_TYPE_S128 16
|
||||
+
|
||||
+static inline struct bcm2708_dmadev *to_bcm2708_dma_dev(struct dma_device *d)
|
||||
+{
|
||||
+ return container_of(d, struct bcm2708_dmadev, ddev);
|
||||
+}
|
||||
+
|
||||
+static inline struct bcm2708_chan *to_bcm2708_dma_chan(struct dma_chan *c)
|
||||
+{
|
||||
+ return container_of(c, struct bcm2708_chan, vc.chan);
|
||||
+}
|
||||
+
|
||||
+static inline struct bcm2708_desc *to_bcm2708_dma_desc(
|
||||
+ struct dma_async_tx_descriptor *t)
|
||||
+{
|
||||
+ return container_of(t, struct bcm2708_desc, vd.tx);
|
||||
+}
|
||||
+
|
||||
+static void bcm2708_dma_desc_free(struct virt_dma_desc *vd)
|
||||
+{
|
||||
+ struct bcm2708_desc *desc = container_of(vd, struct bcm2708_desc, vd);
|
||||
+ dma_free_coherent(desc->vd.tx.chan->device->dev,
|
||||
+ desc->control_block_size,
|
||||
+ desc->control_block_base,
|
||||
+ desc->control_block_base_phys);
|
||||
+ kfree(desc);
|
||||
+}
|
||||
+
|
||||
+static void bcm2708_dma_start_desc(struct bcm2708_chan *c)
|
||||
+{
|
||||
+ struct virt_dma_desc *vd = vchan_next_desc(&c->vc);
|
||||
+ struct bcm2708_desc *d;
|
||||
+
|
||||
+ if (!vd) {
|
||||
+ c->desc = NULL;
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ list_del(&vd->node);
|
||||
+
|
||||
+ c->desc = d = to_bcm2708_dma_desc(&vd->tx);
|
||||
+
|
||||
+ bcm_dma_start(c->chan_base, d->control_block_base_phys);
|
||||
+}
|
||||
+
|
||||
+static irqreturn_t bcm2708_dma_callback(int irq, void *data)
|
||||
+{
|
||||
+ struct bcm2708_chan *c = data;
|
||||
+ struct bcm2708_desc *d;
|
||||
+ unsigned long flags;
|
||||
+
|
||||
+ spin_lock_irqsave(&c->vc.lock, flags);
|
||||
+
|
||||
+ /* Acknowledge interrupt */
|
||||
+ writel(BCM2708_DMA_INT, c->chan_base + BCM2708_DMA_CS);
|
||||
+
|
||||
+ d = c->desc;
|
||||
+
|
||||
+ if (d) {
|
||||
+ /* TODO Only works for cyclic DMA */
|
||||
+ vchan_cyclic_callback(&d->vd);
|
||||
+ }
|
||||
+
|
||||
+ /* Keep the DMA engine running */
|
||||
+ dsb(); /* ARM synchronization barrier */
|
||||
+ writel(BCM2708_DMA_ACTIVE, c->chan_base + BCM2708_DMA_CS);
|
||||
+
|
||||
+ spin_unlock_irqrestore(&c->vc.lock, flags);
|
||||
+
|
||||
+ return IRQ_HANDLED;
|
||||
+}
|
||||
+
|
||||
+static int bcm2708_dma_alloc_chan_resources(struct dma_chan *chan)
|
||||
+{
|
||||
+ struct bcm2708_chan *c = to_bcm2708_dma_chan(chan);
|
||||
+
|
||||
+ return request_irq(c->irq_number,
|
||||
+ bcm2708_dma_callback, 0, "DMA IRQ", c);
|
||||
+}
|
||||
+
|
||||
+static void bcm2708_dma_free_chan_resources(struct dma_chan *chan)
|
||||
+{
|
||||
+ struct bcm2708_chan *c = to_bcm2708_dma_chan(chan);
|
||||
+
|
||||
+ vchan_free_chan_resources(&c->vc);
|
||||
+ free_irq(c->irq_number, c);
|
||||
+
|
||||
+ dev_dbg(c->vc.chan.device->dev, "Freeing DMA channel %u\n", c->ch);
|
||||
+}
|
||||
+
|
||||
+static size_t bcm2708_dma_desc_size(struct bcm2708_desc *d)
|
||||
+{
|
||||
+ return d->size;
|
||||
+}
|
||||
+
|
||||
+static size_t bcm2708_dma_desc_size_pos(struct bcm2708_desc *d, dma_addr_t addr)
|
||||
+{
|
||||
+ unsigned i;
|
||||
+ size_t size;
|
||||
+
|
||||
+ for (size = i = 0; i < d->frames; i++) {
|
||||
+ struct bcm2708_dma_cb *control_block =
|
||||
+ &d->control_block_base[i];
|
||||
+ size_t this_size = control_block->length;
|
||||
+ dma_addr_t dma;
|
||||
+
|
||||
+ if (d->dir == DMA_DEV_TO_MEM)
|
||||
+ dma = control_block->dst;
|
||||
+ else
|
||||
+ dma = control_block->src;
|
||||
+
|
||||
+ if (size)
|
||||
+ size += this_size;
|
||||
+ else if (addr >= dma && addr < dma + this_size)
|
||||
+ size += dma + this_size - addr;
|
||||
+ }
|
||||
+
|
||||
+ return size;
|
||||
+}
|
||||
+
|
||||
+static enum dma_status bcm2708_dma_tx_status(struct dma_chan *chan,
|
||||
+ dma_cookie_t cookie, struct dma_tx_state *txstate)
|
||||
+{
|
||||
+ struct bcm2708_chan *c = to_bcm2708_dma_chan(chan);
|
||||
+ struct virt_dma_desc *vd;
|
||||
+ enum dma_status ret;
|
||||
+ unsigned long flags;
|
||||
+
|
||||
+ ret = dma_cookie_status(chan, cookie, txstate);
|
||||
+ if (ret == DMA_COMPLETE || !txstate)
|
||||
+ return ret;
|
||||
+
|
||||
+ spin_lock_irqsave(&c->vc.lock, flags);
|
||||
+ vd = vchan_find_desc(&c->vc, cookie);
|
||||
+ if (vd) {
|
||||
+ txstate->residue =
|
||||
+ bcm2708_dma_desc_size(to_bcm2708_dma_desc(&vd->tx));
|
||||
+ } else if (c->desc && c->desc->vd.tx.cookie == cookie) {
|
||||
+ struct bcm2708_desc *d = c->desc;
|
||||
+ dma_addr_t pos;
|
||||
+
|
||||
+ if (d->dir == DMA_MEM_TO_DEV)
|
||||
+ pos = readl(c->chan_base + BCM2708_DMA_SOURCE_AD);
|
||||
+ else if (d->dir == DMA_DEV_TO_MEM)
|
||||
+ pos = readl(c->chan_base + BCM2708_DMA_DEST_AD);
|
||||
+ else
|
||||
+ pos = 0;
|
||||
+
|
||||
+ txstate->residue = bcm2708_dma_desc_size_pos(d, pos);
|
||||
+ } else {
|
||||
+ txstate->residue = 0;
|
||||
+ }
|
||||
+
|
||||
+ spin_unlock_irqrestore(&c->vc.lock, flags);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static void bcm2708_dma_issue_pending(struct dma_chan *chan)
|
||||
+{
|
||||
+ struct bcm2708_chan *c = to_bcm2708_dma_chan(chan);
|
||||
+ unsigned long flags;
|
||||
+
|
||||
+ c->cyclic = true; /* Nothing else is implemented */
|
||||
+
|
||||
+ spin_lock_irqsave(&c->vc.lock, flags);
|
||||
+ if (vchan_issue_pending(&c->vc) && !c->desc)
|
||||
+ bcm2708_dma_start_desc(c);
|
||||
+
|
||||
+ spin_unlock_irqrestore(&c->vc.lock, flags);
|
||||
+}
|
||||
+
|
||||
+static struct dma_async_tx_descriptor *bcm2708_dma_prep_dma_cyclic(
|
||||
+ struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
|
||||
+ size_t period_len, enum dma_transfer_direction direction,
|
||||
+ unsigned long flags, void *context)
|
||||
+{
|
||||
+ struct bcm2708_chan *c = to_bcm2708_dma_chan(chan);
|
||||
+ enum dma_slave_buswidth dev_width;
|
||||
+ struct bcm2708_desc *d;
|
||||
+ dma_addr_t dev_addr;
|
||||
+ unsigned es, sync_type;
|
||||
+ unsigned frame;
|
||||
+
|
||||
+ /* Grab configuration */
|
||||
+ if (direction == DMA_DEV_TO_MEM) {
|
||||
+ dev_addr = c->cfg.src_addr;
|
||||
+ dev_width = c->cfg.src_addr_width;
|
||||
+ sync_type = BCM2708_DMA_S_DREQ;
|
||||
+ } else if (direction == DMA_MEM_TO_DEV) {
|
||||
+ dev_addr = c->cfg.dst_addr;
|
||||
+ dev_width = c->cfg.dst_addr_width;
|
||||
+ sync_type = BCM2708_DMA_D_DREQ;
|
||||
+ } else {
|
||||
+ dev_err(chan->device->dev, "%s: bad direction?\n", __func__);
|
||||
+ return NULL;
|
||||
+ }
|
||||
+
|
||||
+ /* Bus width translates to the element size (ES) */
|
||||
+ switch (dev_width) {
|
||||
+ case DMA_SLAVE_BUSWIDTH_4_BYTES:
|
||||
+ es = BCM2708_DMA_DATA_TYPE_S32;
|
||||
+ break;
|
||||
+ default:
|
||||
+ return NULL;
|
||||
+ }
|
||||
+
|
||||
+ /* Now allocate and setup the descriptor. */
|
||||
+ d = kzalloc(sizeof(*d), GFP_NOWAIT);
|
||||
+ if (!d)
|
||||
+ return NULL;
|
||||
+
|
||||
+ d->dir = direction;
|
||||
+ d->frames = buf_len / period_len;
|
||||
+
|
||||
+ /* Allocate memory for control blocks */
|
||||
+ d->control_block_size = d->frames * sizeof(struct bcm2708_dma_cb);
|
||||
+ d->control_block_base = dma_zalloc_coherent(chan->device->dev,
|
||||
+ d->control_block_size, &d->control_block_base_phys,
|
||||
+ GFP_NOWAIT);
|
||||
+
|
||||
+ if (!d->control_block_base) {
|
||||
+ kfree(d);
|
||||
+ return NULL;
|
||||
+ }
|
||||
+
|
||||
+ /*
|
||||
+ * Iterate over all frames, create a control block
|
||||
+ * for each frame and link them together.
|
||||
+ */
|
||||
+ for (frame = 0; frame < d->frames; frame++) {
|
||||
+ struct bcm2708_dma_cb *control_block =
|
||||
+ &d->control_block_base[frame];
|
||||
+
|
||||
+ /* Setup adresses */
|
||||
+ if (d->dir == DMA_DEV_TO_MEM) {
|
||||
+ control_block->info = BCM2708_DMA_D_INC;
|
||||
+ control_block->src = dev_addr;
|
||||
+ control_block->dst = buf_addr + frame * period_len;
|
||||
+ } else {
|
||||
+ control_block->info = BCM2708_DMA_S_INC;
|
||||
+ control_block->src = buf_addr + frame * period_len;
|
||||
+ control_block->dst = dev_addr;
|
||||
+ }
|
||||
+
|
||||
+ /* Enable interrupt */
|
||||
+ control_block->info |= BCM2708_DMA_INT_EN;
|
||||
+
|
||||
+ /* Setup synchronization */
|
||||
+ if (sync_type != 0)
|
||||
+ control_block->info |= sync_type;
|
||||
+
|
||||
+ /* Setup DREQ channel */
|
||||
+ if (c->cfg.slave_id != 0)
|
||||
+ control_block->info |=
|
||||
+ BCM2708_DMA_PER_MAP(c->cfg.slave_id);
|
||||
+
|
||||
+ /* Length of a frame */
|
||||
+ control_block->length = period_len;
|
||||
+ d->size += control_block->length;
|
||||
+
|
||||
+ /*
|
||||
+ * Next block is the next frame.
|
||||
+ * This DMA engine driver currently only supports cyclic DMA.
|
||||
+ * Therefore, wrap around at number of frames.
|
||||
+ */
|
||||
+ control_block->next = d->control_block_base_phys +
|
||||
+ sizeof(struct bcm2708_dma_cb)
|
||||
+ * ((frame + 1) % d->frames);
|
||||
+ }
|
||||
+
|
||||
+ return vchan_tx_prep(&c->vc, &d->vd, flags);
|
||||
+}
|
||||
+
|
||||
+static int bcm2708_dma_slave_config(struct bcm2708_chan *c,
|
||||
+ struct dma_slave_config *cfg)
|
||||
+{
|
||||
+ if ((cfg->direction == DMA_DEV_TO_MEM &&
|
||||
+ cfg->src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES) ||
|
||||
+ (cfg->direction == DMA_MEM_TO_DEV &&
|
||||
+ cfg->dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES) ||
|
||||
+ !is_slave_direction(cfg->direction)) {
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ c->cfg = *cfg;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int bcm2708_dma_terminate_all(struct bcm2708_chan *c)
|
||||
+{
|
||||
+ struct bcm2708_dmadev *d = to_bcm2708_dma_dev(c->vc.chan.device);
|
||||
+ unsigned long flags;
|
||||
+ int timeout = 10000;
|
||||
+ LIST_HEAD(head);
|
||||
+
|
||||
+ spin_lock_irqsave(&c->vc.lock, flags);
|
||||
+
|
||||
+ /* Prevent this channel being scheduled */
|
||||
+ spin_lock(&d->lock);
|
||||
+ list_del_init(&c->node);
|
||||
+ spin_unlock(&d->lock);
|
||||
+
|
||||
+ /*
|
||||
+ * Stop DMA activity: we assume the callback will not be called
|
||||
+ * after bcm_dma_abort() returns (even if it does, it will see
|
||||
+ * c->desc is NULL and exit.)
|
||||
+ */
|
||||
+ if (c->desc) {
|
||||
+ c->desc = NULL;
|
||||
+ bcm_dma_abort(c->chan_base);
|
||||
+
|
||||
+ /* Wait for stopping */
|
||||
+ while (timeout > 0) {
|
||||
+ timeout--;
|
||||
+ if (!(readl(c->chan_base + BCM2708_DMA_CS) &
|
||||
+ BCM2708_DMA_ACTIVE))
|
||||
+ break;
|
||||
+
|
||||
+ cpu_relax();
|
||||
+ }
|
||||
+
|
||||
+ if (timeout <= 0)
|
||||
+ dev_err(d->ddev.dev, "DMA transfer could not be terminated\n");
|
||||
+ }
|
||||
+
|
||||
+ vchan_get_all_descriptors(&c->vc, &head);
|
||||
+ spin_unlock_irqrestore(&c->vc.lock, flags);
|
||||
+ vchan_dma_desc_free_list(&c->vc, &head);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int bcm2708_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
|
||||
+ unsigned long arg)
|
||||
+{
|
||||
+ struct bcm2708_chan *c = to_bcm2708_dma_chan(chan);
|
||||
+
|
||||
+ switch (cmd) {
|
||||
+ case DMA_SLAVE_CONFIG:
|
||||
+ return bcm2708_dma_slave_config(c,
|
||||
+ (struct dma_slave_config *)arg);
|
||||
+
|
||||
+ case DMA_TERMINATE_ALL:
|
||||
+ return bcm2708_dma_terminate_all(c);
|
||||
+
|
||||
+ default:
|
||||
+ return -ENXIO;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static int bcm2708_dma_chan_init(struct bcm2708_dmadev *d, void __iomem* chan_base,
|
||||
+ int chan_id, int irq)
|
||||
+{
|
||||
+ struct bcm2708_chan *c;
|
||||
+
|
||||
+ c = devm_kzalloc(d->ddev.dev, sizeof(*c), GFP_KERNEL);
|
||||
+ if (!c)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ c->vc.desc_free = bcm2708_dma_desc_free;
|
||||
+ vchan_init(&c->vc, &d->ddev);
|
||||
+ INIT_LIST_HEAD(&c->node);
|
||||
+
|
||||
+ d->ddev.chancnt++;
|
||||
+
|
||||
+ c->chan_base = chan_base;
|
||||
+ c->ch = chan_id;
|
||||
+ c->irq_number = irq;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void bcm2708_dma_free(struct bcm2708_dmadev *od)
|
||||
+{
|
||||
+ while (!list_empty(&od->ddev.channels)) {
|
||||
+ struct bcm2708_chan *c = list_first_entry(&od->ddev.channels,
|
||||
+ struct bcm2708_chan, vc.chan.device_node);
|
||||
+
|
||||
+ list_del(&c->vc.chan.device_node);
|
||||
+ tasklet_kill(&c->vc.task);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static int bcm2708_dma_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct bcm2708_dmadev *od;
|
||||
+ int rc, i;
|
||||
+
|
||||
+ if (!pdev->dev.dma_mask)
|
||||
+ pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
|
||||
+
|
||||
+ rc = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
|
||||
+ if (rc)
|
||||
+ return rc;
|
||||
+ dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
|
||||
+
|
||||
+ od = devm_kzalloc(&pdev->dev, sizeof(*od), GFP_KERNEL);
|
||||
+ if (!od)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ pdev->dev.dma_parms = &od->dma_parms;
|
||||
+ dma_set_max_seg_size(&pdev->dev, 0x3FFFFFFF);
|
||||
+
|
||||
+ dma_cap_set(DMA_SLAVE, od->ddev.cap_mask);
|
||||
+ dma_cap_set(DMA_CYCLIC, od->ddev.cap_mask);
|
||||
+ od->ddev.device_alloc_chan_resources = bcm2708_dma_alloc_chan_resources;
|
||||
+ od->ddev.device_free_chan_resources = bcm2708_dma_free_chan_resources;
|
||||
+ od->ddev.device_tx_status = bcm2708_dma_tx_status;
|
||||
+ od->ddev.device_issue_pending = bcm2708_dma_issue_pending;
|
||||
+ od->ddev.device_prep_dma_cyclic = bcm2708_dma_prep_dma_cyclic;
|
||||
+ od->ddev.device_control = bcm2708_dma_control;
|
||||
+ od->ddev.dev = &pdev->dev;
|
||||
+ INIT_LIST_HEAD(&od->ddev.channels);
|
||||
+ spin_lock_init(&od->lock);
|
||||
+
|
||||
+ platform_set_drvdata(pdev, od);
|
||||
+
|
||||
+ for (i = 0; i < 16; i++) {
|
||||
+ void __iomem* chan_base;
|
||||
+ int chan_id, irq;
|
||||
+
|
||||
+ chan_id = bcm_dma_chan_alloc(BCM_DMA_FEATURE_FAST,
|
||||
+ &chan_base,
|
||||
+ &irq);
|
||||
+
|
||||
+ if (chan_id < 0)
|
||||
+ break;
|
||||
+
|
||||
+ rc = bcm2708_dma_chan_init(od, chan_base, chan_id, irq);
|
||||
+ if (rc) {
|
||||
+ bcm2708_dma_free(od);
|
||||
+ return rc;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ rc = dma_async_device_register(&od->ddev);
|
||||
+ if (rc) {
|
||||
+ dev_err(&pdev->dev,
|
||||
+ "Failed to register slave DMA engine device: %d\n", rc);
|
||||
+ bcm2708_dma_free(od);
|
||||
+ return rc;
|
||||
+ }
|
||||
+
|
||||
+ dev_dbg(&pdev->dev, "Load BCM2708 DMA engine driver\n");
|
||||
+
|
||||
+ return rc;
|
||||
+}
|
||||
+
|
||||
+static int bcm2708_dma_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct bcm2708_dmadev *od = platform_get_drvdata(pdev);
|
||||
+
|
||||
+ dma_async_device_unregister(&od->ddev);
|
||||
+ bcm2708_dma_free(od);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static struct platform_driver bcm2708_dma_driver = {
|
||||
+ .probe = bcm2708_dma_probe,
|
||||
+ .remove = bcm2708_dma_remove,
|
||||
+ .driver = {
|
||||
+ .name = "bcm2708-dmaengine",
|
||||
+ .owner = THIS_MODULE,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+static struct platform_device *pdev;
|
||||
+
|
||||
+static const struct platform_device_info bcm2708_dma_dev_info = {
|
||||
+ .name = "bcm2708-dmaengine",
|
||||
+ .id = -1,
|
||||
+};
|
||||
+
|
||||
+static int bcm2708_dma_init(void)
|
||||
+{
|
||||
+ int rc = platform_driver_register(&bcm2708_dma_driver);
|
||||
+
|
||||
+ if (rc == 0) {
|
||||
+ pdev = platform_device_register_full(&bcm2708_dma_dev_info);
|
||||
+ if (IS_ERR(pdev)) {
|
||||
+ platform_driver_unregister(&bcm2708_dma_driver);
|
||||
+ rc = PTR_ERR(pdev);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return rc;
|
||||
+}
|
||||
+subsys_initcall(bcm2708_dma_init);
|
||||
+
|
||||
+static void __exit bcm2708_dma_exit(void)
|
||||
+{
|
||||
+ platform_device_unregister(pdev);
|
||||
+ platform_driver_unregister(&bcm2708_dma_driver);
|
||||
+}
|
||||
+module_exit(bcm2708_dma_exit);
|
||||
+
|
||||
+MODULE_ALIAS("platform:bcm2708-dma");
|
||||
+MODULE_DESCRIPTION("BCM2708 DMA engine driver");
|
||||
+MODULE_AUTHOR("Florian Meier <florian.meier@koalo.de>");
|
||||
+MODULE_LICENSE("GPL v2");
|
|
@ -1,990 +0,0 @@
|
|||
From 8b7d5e646f5b71b22894623419202c9e8c739b2a Mon Sep 17 00:00:00 2001
|
||||
From: Florian Meier <florian.meier@koalo.de>
|
||||
Date: Fri, 22 Nov 2013 14:33:38 +0100
|
||||
Subject: [PATCH 34/54] ASoC: Add support for BCM2708
|
||||
|
||||
This driver adds support for digital audio (I2S)
|
||||
for the BCM2708 SoC that is used by the
|
||||
Raspberry Pi. External audio codecs can be
|
||||
connected to the Raspberry Pi via P5 header.
|
||||
|
||||
It relies on cyclic DMA engine support for BCM2708.
|
||||
|
||||
Signed-off-by: Florian Meier <florian.meier@koalo.de>
|
||||
---
|
||||
sound/soc/bcm/Kconfig | 11 +
|
||||
sound/soc/bcm/Makefile | 4 +
|
||||
sound/soc/bcm/bcm2708-i2s.c | 940 ++++++++++++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 955 insertions(+)
|
||||
create mode 100644 sound/soc/bcm/bcm2708-i2s.c
|
||||
|
||||
--- a/sound/soc/bcm/Kconfig
|
||||
+++ b/sound/soc/bcm/Kconfig
|
||||
@@ -7,3 +7,14 @@ config SND_BCM2835_SOC_I2S
|
||||
Say Y or M if you want to add support for codecs attached to
|
||||
the BCM2835 I2S interface. You will also need
|
||||
to select the audio interfaces to support below.
|
||||
+
|
||||
+config SND_BCM2708_SOC_I2S
|
||||
+ tristate "SoC Audio support for the Broadcom BCM2708 I2S module"
|
||||
+ depends on MACH_BCM2708
|
||||
+ select REGMAP_MMIO
|
||||
+ select SND_SOC_DMAENGINE_PCM
|
||||
+ select SND_SOC_GENERIC_DMAENGINE_PCM
|
||||
+ help
|
||||
+ Say Y or M if you want to add support for codecs attached to
|
||||
+ the BCM2708 I2S interface. You will also need
|
||||
+ to select the audio interfaces to support below.
|
||||
--- a/sound/soc/bcm/Makefile
|
||||
+++ b/sound/soc/bcm/Makefile
|
||||
@@ -3,3 +3,7 @@ snd-soc-bcm2835-i2s-objs := bcm2835-i2s.
|
||||
|
||||
obj-$(CONFIG_SND_BCM2835_SOC_I2S) += snd-soc-bcm2835-i2s.o
|
||||
|
||||
+# BCM2708 Platform Support
|
||||
+snd-soc-bcm2708-i2s-objs := bcm2708-i2s.o
|
||||
+
|
||||
+obj-$(CONFIG_SND_BCM2708_SOC_I2S) += snd-soc-bcm2708-i2s.o
|
||||
--- /dev/null
|
||||
+++ b/sound/soc/bcm/bcm2708-i2s.c
|
||||
@@ -0,0 +1,940 @@
|
||||
+/*
|
||||
+ * ALSA SoC I2S Audio Layer for Broadcom BCM2708 SoC
|
||||
+ *
|
||||
+ * Author: Florian Meier <florian.meier@koalo.de>
|
||||
+ * Copyright 2013
|
||||
+ *
|
||||
+ * Based on
|
||||
+ * Raspberry Pi PCM I2S ALSA Driver
|
||||
+ * Copyright (c) by Phil Poole 2013
|
||||
+ *
|
||||
+ * ALSA SoC I2S (McBSP) Audio Layer for TI DAVINCI processor
|
||||
+ * Vladimir Barinov, <vbarinov@embeddedalley.com>
|
||||
+ * Copyright (C) 2007 MontaVista Software, Inc., <source@mvista.com>
|
||||
+ *
|
||||
+ * OMAP ALSA SoC DAI driver using McBSP port
|
||||
+ * Copyright (C) 2008 Nokia Corporation
|
||||
+ * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com>
|
||||
+ * Peter Ujfalusi <peter.ujfalusi@ti.com>
|
||||
+ *
|
||||
+ * Freescale SSI ALSA SoC Digital Audio Interface (DAI) driver
|
||||
+ * Author: Timur Tabi <timur@freescale.com>
|
||||
+ * Copyright 2007-2010 Freescale Semiconductor, Inc.
|
||||
+ *
|
||||
+ * 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/init.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/device.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/delay.h>
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/clk.h>
|
||||
+
|
||||
+#include <sound/core.h>
|
||||
+#include <sound/pcm.h>
|
||||
+#include <sound/pcm_params.h>
|
||||
+#include <sound/initval.h>
|
||||
+#include <sound/soc.h>
|
||||
+#include <sound/dmaengine_pcm.h>
|
||||
+
|
||||
+/* Clock registers */
|
||||
+#define BCM2708_CLK_PCMCTL_REG 0x00
|
||||
+#define BCM2708_CLK_PCMDIV_REG 0x04
|
||||
+
|
||||
+/* Clock register settings */
|
||||
+#define BCM2708_CLK_PASSWD (0x5a000000)
|
||||
+#define BCM2708_CLK_PASSWD_MASK (0xff000000)
|
||||
+#define BCM2708_CLK_MASH(v) ((v) << 9)
|
||||
+#define BCM2708_CLK_FLIP BIT(8)
|
||||
+#define BCM2708_CLK_BUSY BIT(7)
|
||||
+#define BCM2708_CLK_KILL BIT(5)
|
||||
+#define BCM2708_CLK_ENAB BIT(4)
|
||||
+#define BCM2708_CLK_SRC(v) (v)
|
||||
+
|
||||
+#define BCM2708_CLK_SHIFT (12)
|
||||
+#define BCM2708_CLK_DIVI(v) ((v) << BCM2708_CLK_SHIFT)
|
||||
+#define BCM2708_CLK_DIVF(v) (v)
|
||||
+#define BCM2708_CLK_DIVF_MASK (0xFFF)
|
||||
+
|
||||
+enum {
|
||||
+ BCM2708_CLK_MASH_0 = 0,
|
||||
+ BCM2708_CLK_MASH_1,
|
||||
+ BCM2708_CLK_MASH_2,
|
||||
+ BCM2708_CLK_MASH_3,
|
||||
+};
|
||||
+
|
||||
+enum {
|
||||
+ BCM2708_CLK_SRC_GND = 0,
|
||||
+ BCM2708_CLK_SRC_OSC,
|
||||
+ BCM2708_CLK_SRC_DBG0,
|
||||
+ BCM2708_CLK_SRC_DBG1,
|
||||
+ BCM2708_CLK_SRC_PLLA,
|
||||
+ BCM2708_CLK_SRC_PLLC,
|
||||
+ BCM2708_CLK_SRC_PLLD,
|
||||
+ BCM2708_CLK_SRC_HDMI,
|
||||
+};
|
||||
+
|
||||
+/* Most clocks are not useable (freq = 0) */
|
||||
+static const unsigned int bcm2708_clk_freq[BCM2708_CLK_SRC_HDMI+1] = {
|
||||
+ [BCM2708_CLK_SRC_GND] = 0,
|
||||
+ [BCM2708_CLK_SRC_OSC] = 19200000,
|
||||
+ [BCM2708_CLK_SRC_DBG0] = 0,
|
||||
+ [BCM2708_CLK_SRC_DBG1] = 0,
|
||||
+ [BCM2708_CLK_SRC_PLLA] = 0,
|
||||
+ [BCM2708_CLK_SRC_PLLC] = 0,
|
||||
+ [BCM2708_CLK_SRC_PLLD] = 500000000,
|
||||
+ [BCM2708_CLK_SRC_HDMI] = 0,
|
||||
+};
|
||||
+
|
||||
+/* I2S registers */
|
||||
+#define BCM2708_I2S_CS_A_REG 0x00
|
||||
+#define BCM2708_I2S_FIFO_A_REG 0x04
|
||||
+#define BCM2708_I2S_MODE_A_REG 0x08
|
||||
+#define BCM2708_I2S_RXC_A_REG 0x0c
|
||||
+#define BCM2708_I2S_TXC_A_REG 0x10
|
||||
+#define BCM2708_I2S_DREQ_A_REG 0x14
|
||||
+#define BCM2708_I2S_INTEN_A_REG 0x18
|
||||
+#define BCM2708_I2S_INTSTC_A_REG 0x1c
|
||||
+#define BCM2708_I2S_GRAY_REG 0x20
|
||||
+
|
||||
+/* I2S register settings */
|
||||
+#define BCM2708_I2S_STBY BIT(25)
|
||||
+#define BCM2708_I2S_SYNC BIT(24)
|
||||
+#define BCM2708_I2S_RXSEX BIT(23)
|
||||
+#define BCM2708_I2S_RXF BIT(22)
|
||||
+#define BCM2708_I2S_TXE BIT(21)
|
||||
+#define BCM2708_I2S_RXD BIT(20)
|
||||
+#define BCM2708_I2S_TXD BIT(19)
|
||||
+#define BCM2708_I2S_RXR BIT(18)
|
||||
+#define BCM2708_I2S_TXW BIT(17)
|
||||
+#define BCM2708_I2S_CS_RXERR BIT(16)
|
||||
+#define BCM2708_I2S_CS_TXERR BIT(15)
|
||||
+#define BCM2708_I2S_RXSYNC BIT(14)
|
||||
+#define BCM2708_I2S_TXSYNC BIT(13)
|
||||
+#define BCM2708_I2S_DMAEN BIT(9)
|
||||
+#define BCM2708_I2S_RXTHR(v) ((v) << 7)
|
||||
+#define BCM2708_I2S_TXTHR(v) ((v) << 5)
|
||||
+#define BCM2708_I2S_RXCLR BIT(4)
|
||||
+#define BCM2708_I2S_TXCLR BIT(3)
|
||||
+#define BCM2708_I2S_TXON BIT(2)
|
||||
+#define BCM2708_I2S_RXON BIT(1)
|
||||
+#define BCM2708_I2S_EN (1)
|
||||
+
|
||||
+#define BCM2708_I2S_CLKDIS BIT(28)
|
||||
+#define BCM2708_I2S_PDMN BIT(27)
|
||||
+#define BCM2708_I2S_PDME BIT(26)
|
||||
+#define BCM2708_I2S_FRXP BIT(25)
|
||||
+#define BCM2708_I2S_FTXP BIT(24)
|
||||
+#define BCM2708_I2S_CLKM BIT(23)
|
||||
+#define BCM2708_I2S_CLKI BIT(22)
|
||||
+#define BCM2708_I2S_FSM BIT(21)
|
||||
+#define BCM2708_I2S_FSI BIT(20)
|
||||
+#define BCM2708_I2S_FLEN(v) ((v) << 10)
|
||||
+#define BCM2708_I2S_FSLEN(v) (v)
|
||||
+
|
||||
+#define BCM2708_I2S_CHWEX BIT(15)
|
||||
+#define BCM2708_I2S_CHEN BIT(14)
|
||||
+#define BCM2708_I2S_CHPOS(v) ((v) << 4)
|
||||
+#define BCM2708_I2S_CHWID(v) (v)
|
||||
+#define BCM2708_I2S_CH1(v) ((v) << 16)
|
||||
+#define BCM2708_I2S_CH2(v) (v)
|
||||
+
|
||||
+#define BCM2708_I2S_TX_PANIC(v) ((v) << 24)
|
||||
+#define BCM2708_I2S_RX_PANIC(v) ((v) << 16)
|
||||
+#define BCM2708_I2S_TX(v) ((v) << 8)
|
||||
+#define BCM2708_I2S_RX(v) (v)
|
||||
+
|
||||
+#define BCM2708_I2S_INT_RXERR BIT(3)
|
||||
+#define BCM2708_I2S_INT_TXERR BIT(2)
|
||||
+#define BCM2708_I2S_INT_RXR BIT(1)
|
||||
+#define BCM2708_I2S_INT_TXW BIT(0)
|
||||
+
|
||||
+/* I2S DMA interface */
|
||||
+#define BCM2708_I2S_FIFO_PHYSICAL_ADDR 0x7E203004
|
||||
+#define BCM2708_DMA_DREQ_PCM_TX 2
|
||||
+#define BCM2708_DMA_DREQ_PCM_RX 3
|
||||
+
|
||||
+/* General device struct */
|
||||
+struct bcm2708_i2s_dev {
|
||||
+ struct device *dev;
|
||||
+ struct snd_dmaengine_dai_dma_data dma_data[2];
|
||||
+ unsigned int fmt;
|
||||
+ unsigned int bclk_ratio;
|
||||
+
|
||||
+ struct regmap *i2s_regmap;
|
||||
+ struct regmap *clk_regmap;
|
||||
+};
|
||||
+
|
||||
+static void bcm2708_i2s_start_clock(struct bcm2708_i2s_dev *dev)
|
||||
+{
|
||||
+ /* Start the clock if in master mode */
|
||||
+ unsigned int master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK;
|
||||
+
|
||||
+ switch (master) {
|
||||
+ case SND_SOC_DAIFMT_CBS_CFS:
|
||||
+ case SND_SOC_DAIFMT_CBS_CFM:
|
||||
+ regmap_update_bits(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG,
|
||||
+ BCM2708_CLK_PASSWD_MASK | BCM2708_CLK_ENAB,
|
||||
+ BCM2708_CLK_PASSWD | BCM2708_CLK_ENAB);
|
||||
+ break;
|
||||
+ default:
|
||||
+ break;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void bcm2708_i2s_stop_clock(struct bcm2708_i2s_dev *dev)
|
||||
+{
|
||||
+ uint32_t clkreg;
|
||||
+ int timeout = 1000;
|
||||
+
|
||||
+ /* Stop clock */
|
||||
+ regmap_update_bits(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG,
|
||||
+ BCM2708_CLK_PASSWD_MASK | BCM2708_CLK_ENAB,
|
||||
+ BCM2708_CLK_PASSWD);
|
||||
+
|
||||
+ /* Wait for the BUSY flag going down */
|
||||
+ while (--timeout) {
|
||||
+ regmap_read(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, &clkreg);
|
||||
+ if (!(clkreg & BCM2708_CLK_BUSY))
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ if (!timeout) {
|
||||
+ /* KILL the clock */
|
||||
+ dev_err(dev->dev, "I2S clock didn't stop. Kill the clock!\n");
|
||||
+ regmap_update_bits(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG,
|
||||
+ BCM2708_CLK_KILL | BCM2708_CLK_PASSWD_MASK,
|
||||
+ BCM2708_CLK_KILL | BCM2708_CLK_PASSWD);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void bcm2708_i2s_clear_fifos(struct bcm2708_i2s_dev *dev,
|
||||
+ bool tx, bool rx)
|
||||
+{
|
||||
+ int timeout = 1000;
|
||||
+ uint32_t syncval;
|
||||
+ uint32_t csreg;
|
||||
+ uint32_t i2s_active_state;
|
||||
+ uint32_t clkreg;
|
||||
+ uint32_t clk_active_state;
|
||||
+ uint32_t off;
|
||||
+ uint32_t clr;
|
||||
+
|
||||
+ off = tx ? BCM2708_I2S_TXON : 0;
|
||||
+ off |= rx ? BCM2708_I2S_RXON : 0;
|
||||
+
|
||||
+ clr = tx ? BCM2708_I2S_TXCLR : 0;
|
||||
+ clr |= rx ? BCM2708_I2S_RXCLR : 0;
|
||||
+
|
||||
+ /* Backup the current state */
|
||||
+ regmap_read(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, &csreg);
|
||||
+ i2s_active_state = csreg & (BCM2708_I2S_RXON | BCM2708_I2S_TXON);
|
||||
+
|
||||
+ regmap_read(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, &clkreg);
|
||||
+ clk_active_state = clkreg & BCM2708_CLK_ENAB;
|
||||
+
|
||||
+ /* Start clock if not running */
|
||||
+ if (!clk_active_state) {
|
||||
+ regmap_update_bits(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG,
|
||||
+ BCM2708_CLK_PASSWD_MASK | BCM2708_CLK_ENAB,
|
||||
+ BCM2708_CLK_PASSWD | BCM2708_CLK_ENAB);
|
||||
+ }
|
||||
+
|
||||
+ /* Stop I2S module */
|
||||
+ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, off, 0);
|
||||
+
|
||||
+ /*
|
||||
+ * Clear the FIFOs
|
||||
+ * Requires at least 2 PCM clock cycles to take effect
|
||||
+ */
|
||||
+ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, clr, clr);
|
||||
+
|
||||
+ /* Wait for 2 PCM clock cycles */
|
||||
+
|
||||
+ /*
|
||||
+ * Toggle the SYNC flag. After 2 PCM clock cycles it can be read back
|
||||
+ * FIXME: This does not seem to work for slave mode!
|
||||
+ */
|
||||
+ regmap_read(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, &syncval);
|
||||
+ syncval &= BCM2708_I2S_SYNC;
|
||||
+
|
||||
+ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG,
|
||||
+ BCM2708_I2S_SYNC, ~syncval);
|
||||
+
|
||||
+ /* Wait for the SYNC flag changing it's state */
|
||||
+ while (--timeout) {
|
||||
+ regmap_read(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, &csreg);
|
||||
+ if ((csreg & BCM2708_I2S_SYNC) != syncval)
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ if (!timeout)
|
||||
+ dev_err(dev->dev, "I2S SYNC error!\n");
|
||||
+
|
||||
+ /* Stop clock if it was not running before */
|
||||
+ if (!clk_active_state)
|
||||
+ bcm2708_i2s_stop_clock(dev);
|
||||
+
|
||||
+ /* Restore I2S state */
|
||||
+ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG,
|
||||
+ BCM2708_I2S_RXON | BCM2708_I2S_TXON, i2s_active_state);
|
||||
+}
|
||||
+
|
||||
+static int bcm2708_i2s_set_dai_fmt(struct snd_soc_dai *dai,
|
||||
+ unsigned int fmt)
|
||||
+{
|
||||
+ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
|
||||
+ dev->fmt = fmt;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int bcm2708_i2s_set_dai_bclk_ratio(struct snd_soc_dai *dai,
|
||||
+ unsigned int ratio)
|
||||
+{
|
||||
+ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
|
||||
+ dev->bclk_ratio = ratio;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int bcm2708_i2s_hw_params(struct snd_pcm_substream *substream,
|
||||
+ struct snd_pcm_hw_params *params,
|
||||
+ struct snd_soc_dai *dai)
|
||||
+{
|
||||
+ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
|
||||
+
|
||||
+ unsigned int sampling_rate = params_rate(params);
|
||||
+ unsigned int data_length, data_delay, bclk_ratio;
|
||||
+ unsigned int ch1pos, ch2pos, mode, format;
|
||||
+ unsigned int mash = BCM2708_CLK_MASH_1;
|
||||
+ unsigned int divi, divf, target_frequency;
|
||||
+ int clk_src = -1;
|
||||
+ unsigned int master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK;
|
||||
+ bool bit_master = (master == SND_SOC_DAIFMT_CBS_CFS
|
||||
+ || master == SND_SOC_DAIFMT_CBS_CFM);
|
||||
+
|
||||
+ bool frame_master = (master == SND_SOC_DAIFMT_CBS_CFS
|
||||
+ || master == SND_SOC_DAIFMT_CBM_CFS);
|
||||
+ uint32_t csreg;
|
||||
+
|
||||
+ /*
|
||||
+ * If a stream is already enabled,
|
||||
+ * the registers are already set properly.
|
||||
+ */
|
||||
+ regmap_read(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, &csreg);
|
||||
+
|
||||
+ if (csreg & (BCM2708_I2S_TXON | BCM2708_I2S_RXON))
|
||||
+ return 0;
|
||||
+
|
||||
+ /*
|
||||
+ * Adjust the data length according to the format.
|
||||
+ * We prefill the half frame length with an integer
|
||||
+ * divider of 2400 as explained at the clock settings.
|
||||
+ * Maybe it is overwritten there, if the Integer mode
|
||||
+ * does not apply.
|
||||
+ */
|
||||
+ switch (params_format(params)) {
|
||||
+ case SNDRV_PCM_FORMAT_S16_LE:
|
||||
+ data_length = 16;
|
||||
+ bclk_ratio = 40;
|
||||
+ break;
|
||||
+ case SNDRV_PCM_FORMAT_S32_LE:
|
||||
+ data_length = 32;
|
||||
+ bclk_ratio = 80;
|
||||
+ break;
|
||||
+ default:
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ /* If bclk_ratio already set, use that one. */
|
||||
+ if (dev->bclk_ratio)
|
||||
+ bclk_ratio = dev->bclk_ratio;
|
||||
+
|
||||
+ /*
|
||||
+ * Clock Settings
|
||||
+ *
|
||||
+ * The target frequency of the bit clock is
|
||||
+ * sampling rate * frame length
|
||||
+ *
|
||||
+ * Integer mode:
|
||||
+ * Sampling rates that are multiples of 8000 kHz
|
||||
+ * can be driven by the oscillator of 19.2 MHz
|
||||
+ * with an integer divider as long as the frame length
|
||||
+ * is an integer divider of 19200000/8000=2400 as set up above.
|
||||
+ * This is no longer possible if the sampling rate
|
||||
+ * is too high (e.g. 192 kHz), because the oscillator is too slow.
|
||||
+ *
|
||||
+ * MASH mode:
|
||||
+ * For all other sampling rates, it is not possible to
|
||||
+ * have an integer divider. Approximate the clock
|
||||
+ * with the MASH module that induces a slight frequency
|
||||
+ * variance. To minimize that it is best to have the fastest
|
||||
+ * clock here. That is PLLD with 500 MHz.
|
||||
+ */
|
||||
+ target_frequency = sampling_rate * bclk_ratio;
|
||||
+ clk_src = BCM2708_CLK_SRC_OSC;
|
||||
+ mash = BCM2708_CLK_MASH_0;
|
||||
+
|
||||
+ if (bcm2708_clk_freq[clk_src] % target_frequency == 0
|
||||
+ && bit_master && frame_master) {
|
||||
+ divi = bcm2708_clk_freq[clk_src] / target_frequency;
|
||||
+ divf = 0;
|
||||
+ } else {
|
||||
+ uint64_t dividend;
|
||||
+
|
||||
+ if (!dev->bclk_ratio) {
|
||||
+ /*
|
||||
+ * Overwrite bclk_ratio, because the
|
||||
+ * above trick is not needed or can
|
||||
+ * not be used.
|
||||
+ */
|
||||
+ bclk_ratio = 2 * data_length;
|
||||
+ }
|
||||
+
|
||||
+ target_frequency = sampling_rate * bclk_ratio;
|
||||
+
|
||||
+ clk_src = BCM2708_CLK_SRC_PLLD;
|
||||
+ mash = BCM2708_CLK_MASH_1;
|
||||
+
|
||||
+ dividend = bcm2708_clk_freq[clk_src];
|
||||
+ dividend <<= BCM2708_CLK_SHIFT;
|
||||
+ do_div(dividend, target_frequency);
|
||||
+ divi = dividend >> BCM2708_CLK_SHIFT;
|
||||
+ divf = dividend & BCM2708_CLK_DIVF_MASK;
|
||||
+ }
|
||||
+
|
||||
+ /* Set clock divider */
|
||||
+ regmap_write(dev->clk_regmap, BCM2708_CLK_PCMDIV_REG, BCM2708_CLK_PASSWD
|
||||
+ | BCM2708_CLK_DIVI(divi)
|
||||
+ | BCM2708_CLK_DIVF(divf));
|
||||
+
|
||||
+ /* Setup clock, but don't start it yet */
|
||||
+ regmap_write(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, BCM2708_CLK_PASSWD
|
||||
+ | BCM2708_CLK_MASH(mash)
|
||||
+ | BCM2708_CLK_SRC(clk_src));
|
||||
+
|
||||
+ /* Setup the frame format */
|
||||
+ format = BCM2708_I2S_CHEN;
|
||||
+
|
||||
+ if (data_length > 24)
|
||||
+ format |= BCM2708_I2S_CHWEX;
|
||||
+
|
||||
+ format |= BCM2708_I2S_CHWID((data_length-8)&0xf);
|
||||
+
|
||||
+ switch (dev->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
+ case SND_SOC_DAIFMT_I2S:
|
||||
+ data_delay = 1;
|
||||
+ break;
|
||||
+ default:
|
||||
+ /*
|
||||
+ * TODO
|
||||
+ * Others are possible but are not implemented at the moment.
|
||||
+ */
|
||||
+ dev_err(dev->dev, "%s:bad format\n", __func__);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ ch1pos = data_delay;
|
||||
+ ch2pos = bclk_ratio / 2 + data_delay;
|
||||
+
|
||||
+ switch (params_channels(params)) {
|
||||
+ case 2:
|
||||
+ format = BCM2708_I2S_CH1(format) | BCM2708_I2S_CH2(format);
|
||||
+ format |= BCM2708_I2S_CH1(BCM2708_I2S_CHPOS(ch1pos));
|
||||
+ format |= BCM2708_I2S_CH2(BCM2708_I2S_CHPOS(ch2pos));
|
||||
+ break;
|
||||
+ default:
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ /*
|
||||
+ * Set format for both streams.
|
||||
+ * We cannot set another frame length
|
||||
+ * (and therefore word length) anyway,
|
||||
+ * so the format will be the same.
|
||||
+ */
|
||||
+ regmap_write(dev->i2s_regmap, BCM2708_I2S_RXC_A_REG, format);
|
||||
+ regmap_write(dev->i2s_regmap, BCM2708_I2S_TXC_A_REG, format);
|
||||
+
|
||||
+ /* Setup the I2S mode */
|
||||
+ mode = 0;
|
||||
+
|
||||
+ if (data_length <= 16) {
|
||||
+ /*
|
||||
+ * Use frame packed mode (2 channels per 32 bit word)
|
||||
+ * We cannot set another frame length in the second stream
|
||||
+ * (and therefore word length) anyway,
|
||||
+ * so the format will be the same.
|
||||
+ */
|
||||
+ mode |= BCM2708_I2S_FTXP | BCM2708_I2S_FRXP;
|
||||
+ }
|
||||
+
|
||||
+ mode |= BCM2708_I2S_FLEN(bclk_ratio - 1);
|
||||
+ mode |= BCM2708_I2S_FSLEN(bclk_ratio / 2);
|
||||
+
|
||||
+ /* Master or slave? */
|
||||
+ switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
+ case SND_SOC_DAIFMT_CBS_CFS:
|
||||
+ /* CPU is master */
|
||||
+ break;
|
||||
+ case SND_SOC_DAIFMT_CBM_CFS:
|
||||
+ /*
|
||||
+ * CODEC is bit clock master
|
||||
+ * CPU is frame master
|
||||
+ */
|
||||
+ mode |= BCM2708_I2S_CLKM;
|
||||
+ break;
|
||||
+ case SND_SOC_DAIFMT_CBS_CFM:
|
||||
+ /*
|
||||
+ * CODEC is frame master
|
||||
+ * CPU is bit clock master
|
||||
+ */
|
||||
+ mode |= BCM2708_I2S_FSM;
|
||||
+ break;
|
||||
+ case SND_SOC_DAIFMT_CBM_CFM:
|
||||
+ /* CODEC is master */
|
||||
+ mode |= BCM2708_I2S_CLKM;
|
||||
+ mode |= BCM2708_I2S_FSM;
|
||||
+ break;
|
||||
+ default:
|
||||
+ dev_err(dev->dev, "%s:bad master\n", __func__);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ /*
|
||||
+ * Invert clocks?
|
||||
+ *
|
||||
+ * The BCM approach seems to be inverted to the classical I2S approach.
|
||||
+ */
|
||||
+ switch (dev->fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
+ case SND_SOC_DAIFMT_NB_NF:
|
||||
+ /* None. Therefore, both for BCM */
|
||||
+ mode |= BCM2708_I2S_CLKI;
|
||||
+ mode |= BCM2708_I2S_FSI;
|
||||
+ break;
|
||||
+ case SND_SOC_DAIFMT_IB_IF:
|
||||
+ /* Both. Therefore, none for BCM */
|
||||
+ break;
|
||||
+ case SND_SOC_DAIFMT_NB_IF:
|
||||
+ /*
|
||||
+ * Invert only frame sync. Therefore,
|
||||
+ * invert only bit clock for BCM
|
||||
+ */
|
||||
+ mode |= BCM2708_I2S_CLKI;
|
||||
+ break;
|
||||
+ case SND_SOC_DAIFMT_IB_NF:
|
||||
+ /*
|
||||
+ * Invert only bit clock. Therefore,
|
||||
+ * invert only frame sync for BCM
|
||||
+ */
|
||||
+ mode |= BCM2708_I2S_FSI;
|
||||
+ break;
|
||||
+ default:
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ regmap_write(dev->i2s_regmap, BCM2708_I2S_MODE_A_REG, mode);
|
||||
+
|
||||
+ /* Setup the DMA parameters */
|
||||
+ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG,
|
||||
+ BCM2708_I2S_RXTHR(1)
|
||||
+ | BCM2708_I2S_TXTHR(1)
|
||||
+ | BCM2708_I2S_DMAEN, 0xffffffff);
|
||||
+
|
||||
+ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_DREQ_A_REG,
|
||||
+ BCM2708_I2S_TX_PANIC(0x10)
|
||||
+ | BCM2708_I2S_RX_PANIC(0x30)
|
||||
+ | BCM2708_I2S_TX(0x30)
|
||||
+ | BCM2708_I2S_RX(0x20), 0xffffffff);
|
||||
+
|
||||
+ /* Clear FIFOs */
|
||||
+ bcm2708_i2s_clear_fifos(dev, true, true);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int bcm2708_i2s_prepare(struct snd_pcm_substream *substream,
|
||||
+ struct snd_soc_dai *dai)
|
||||
+{
|
||||
+ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
|
||||
+ uint32_t cs_reg;
|
||||
+
|
||||
+ bcm2708_i2s_start_clock(dev);
|
||||
+
|
||||
+ /*
|
||||
+ * Clear both FIFOs if the one that should be started
|
||||
+ * is not empty at the moment. This should only happen
|
||||
+ * after overrun. Otherwise, hw_params would have cleared
|
||||
+ * the FIFO.
|
||||
+ */
|
||||
+ regmap_read(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, &cs_reg);
|
||||
+
|
||||
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK
|
||||
+ && !(cs_reg & BCM2708_I2S_TXE))
|
||||
+ bcm2708_i2s_clear_fifos(dev, true, false);
|
||||
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE
|
||||
+ && (cs_reg & BCM2708_I2S_RXD))
|
||||
+ bcm2708_i2s_clear_fifos(dev, false, true);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void bcm2708_i2s_stop(struct bcm2708_i2s_dev *dev,
|
||||
+ struct snd_pcm_substream *substream,
|
||||
+ struct snd_soc_dai *dai)
|
||||
+{
|
||||
+ uint32_t mask;
|
||||
+
|
||||
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
+ mask = BCM2708_I2S_RXON;
|
||||
+ else
|
||||
+ mask = BCM2708_I2S_TXON;
|
||||
+
|
||||
+ regmap_update_bits(dev->i2s_regmap,
|
||||
+ BCM2708_I2S_CS_A_REG, mask, 0);
|
||||
+
|
||||
+ /* Stop also the clock when not SND_SOC_DAIFMT_CONT */
|
||||
+ if (!dai->active && !(dev->fmt & SND_SOC_DAIFMT_CONT))
|
||||
+ bcm2708_i2s_stop_clock(dev);
|
||||
+}
|
||||
+
|
||||
+static int bcm2708_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
+ struct snd_soc_dai *dai)
|
||||
+{
|
||||
+ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
|
||||
+ uint32_t mask;
|
||||
+
|
||||
+ switch (cmd) {
|
||||
+ case SNDRV_PCM_TRIGGER_START:
|
||||
+ case SNDRV_PCM_TRIGGER_RESUME:
|
||||
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
+ bcm2708_i2s_start_clock(dev);
|
||||
+
|
||||
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
+ mask = BCM2708_I2S_RXON;
|
||||
+ else
|
||||
+ mask = BCM2708_I2S_TXON;
|
||||
+
|
||||
+ regmap_update_bits(dev->i2s_regmap,
|
||||
+ BCM2708_I2S_CS_A_REG, mask, mask);
|
||||
+ break;
|
||||
+
|
||||
+ case SNDRV_PCM_TRIGGER_STOP:
|
||||
+ case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
+ bcm2708_i2s_stop(dev, substream, dai);
|
||||
+ break;
|
||||
+ default:
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int bcm2708_i2s_startup(struct snd_pcm_substream *substream,
|
||||
+ struct snd_soc_dai *dai)
|
||||
+{
|
||||
+ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
|
||||
+
|
||||
+ if (dai->active)
|
||||
+ return 0;
|
||||
+
|
||||
+ /* Should this still be running stop it */
|
||||
+ bcm2708_i2s_stop_clock(dev);
|
||||
+
|
||||
+ /* Enable PCM block */
|
||||
+ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG,
|
||||
+ BCM2708_I2S_EN, BCM2708_I2S_EN);
|
||||
+
|
||||
+ /*
|
||||
+ * Disable STBY.
|
||||
+ * Requires at least 4 PCM clock cycles to take effect.
|
||||
+ */
|
||||
+ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG,
|
||||
+ BCM2708_I2S_STBY, BCM2708_I2S_STBY);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void bcm2708_i2s_shutdown(struct snd_pcm_substream *substream,
|
||||
+ struct snd_soc_dai *dai)
|
||||
+{
|
||||
+ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
|
||||
+
|
||||
+ bcm2708_i2s_stop(dev, substream, dai);
|
||||
+
|
||||
+ /* If both streams are stopped, disable module and clock */
|
||||
+ if (dai->active)
|
||||
+ return;
|
||||
+
|
||||
+ /* Disable the module */
|
||||
+ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG,
|
||||
+ BCM2708_I2S_EN, 0);
|
||||
+
|
||||
+ /*
|
||||
+ * Stopping clock is necessary, because stop does
|
||||
+ * not stop the clock when SND_SOC_DAIFMT_CONT
|
||||
+ */
|
||||
+ bcm2708_i2s_stop_clock(dev);
|
||||
+}
|
||||
+
|
||||
+static const struct snd_soc_dai_ops bcm2708_i2s_dai_ops = {
|
||||
+ .startup = bcm2708_i2s_startup,
|
||||
+ .shutdown = bcm2708_i2s_shutdown,
|
||||
+ .prepare = bcm2708_i2s_prepare,
|
||||
+ .trigger = bcm2708_i2s_trigger,
|
||||
+ .hw_params = bcm2708_i2s_hw_params,
|
||||
+ .set_fmt = bcm2708_i2s_set_dai_fmt,
|
||||
+ .set_bclk_ratio = bcm2708_i2s_set_dai_bclk_ratio
|
||||
+};
|
||||
+
|
||||
+static int bcm2708_i2s_dai_probe(struct snd_soc_dai *dai)
|
||||
+{
|
||||
+ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
|
||||
+
|
||||
+ dai->playback_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK];
|
||||
+ dai->capture_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE];
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static struct snd_soc_dai_driver bcm2708_i2s_dai = {
|
||||
+ .name = "bcm2708-i2s",
|
||||
+ .probe = bcm2708_i2s_dai_probe,
|
||||
+ .playback = {
|
||||
+ .channels_min = 2,
|
||||
+ .channels_max = 2,
|
||||
+ .rates = SNDRV_PCM_RATE_8000_192000,
|
||||
+ .formats = SNDRV_PCM_FMTBIT_S16_LE
|
||||
+ | SNDRV_PCM_FMTBIT_S32_LE
|
||||
+ },
|
||||
+ .capture = {
|
||||
+ .channels_min = 2,
|
||||
+ .channels_max = 2,
|
||||
+ .rates = SNDRV_PCM_RATE_8000_192000,
|
||||
+ .formats = SNDRV_PCM_FMTBIT_S16_LE
|
||||
+ | SNDRV_PCM_FMTBIT_S32_LE
|
||||
+ },
|
||||
+ .ops = &bcm2708_i2s_dai_ops,
|
||||
+ .symmetric_rates = 1
|
||||
+};
|
||||
+
|
||||
+static bool bcm2708_i2s_volatile_reg(struct device *dev, unsigned int reg)
|
||||
+{
|
||||
+ switch (reg) {
|
||||
+ case BCM2708_I2S_CS_A_REG:
|
||||
+ case BCM2708_I2S_FIFO_A_REG:
|
||||
+ case BCM2708_I2S_INTSTC_A_REG:
|
||||
+ case BCM2708_I2S_GRAY_REG:
|
||||
+ return true;
|
||||
+ default:
|
||||
+ return false;
|
||||
+ };
|
||||
+}
|
||||
+
|
||||
+static bool bcm2708_i2s_precious_reg(struct device *dev, unsigned int reg)
|
||||
+{
|
||||
+ switch (reg) {
|
||||
+ case BCM2708_I2S_FIFO_A_REG:
|
||||
+ return true;
|
||||
+ default:
|
||||
+ return false;
|
||||
+ };
|
||||
+}
|
||||
+
|
||||
+static bool bcm2708_clk_volatile_reg(struct device *dev, unsigned int reg)
|
||||
+{
|
||||
+ switch (reg) {
|
||||
+ case BCM2708_CLK_PCMCTL_REG:
|
||||
+ return true;
|
||||
+ default:
|
||||
+ return false;
|
||||
+ };
|
||||
+}
|
||||
+
|
||||
+static const struct regmap_config bcm2708_regmap_config[] = {
|
||||
+ {
|
||||
+ .reg_bits = 32,
|
||||
+ .reg_stride = 4,
|
||||
+ .val_bits = 32,
|
||||
+ .max_register = BCM2708_I2S_GRAY_REG,
|
||||
+ .precious_reg = bcm2708_i2s_precious_reg,
|
||||
+ .volatile_reg = bcm2708_i2s_volatile_reg,
|
||||
+ .cache_type = REGCACHE_RBTREE,
|
||||
+ },
|
||||
+ {
|
||||
+ .reg_bits = 32,
|
||||
+ .reg_stride = 4,
|
||||
+ .val_bits = 32,
|
||||
+ .max_register = BCM2708_CLK_PCMDIV_REG,
|
||||
+ .volatile_reg = bcm2708_clk_volatile_reg,
|
||||
+ .cache_type = REGCACHE_RBTREE,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+static const struct snd_soc_component_driver bcm2708_i2s_component = {
|
||||
+ .name = "bcm2708-i2s-comp",
|
||||
+};
|
||||
+
|
||||
+
|
||||
+static void bcm2708_i2s_setup_gpio(void)
|
||||
+{
|
||||
+ /*
|
||||
+ * This is the common way to handle the GPIO pins for
|
||||
+ * the Raspberry Pi.
|
||||
+ * TODO Better way would be to handle
|
||||
+ * this in the device tree!
|
||||
+ */
|
||||
+#define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3))
|
||||
+#define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3))
|
||||
+
|
||||
+ unsigned int *gpio;
|
||||
+ int pin;
|
||||
+ gpio = ioremap(GPIO_BASE, SZ_16K);
|
||||
+
|
||||
+ /* SPI is on GPIO 7..11 */
|
||||
+ for (pin = 28; pin <= 31; pin++) {
|
||||
+ INP_GPIO(pin); /* set mode to GPIO input first */
|
||||
+ SET_GPIO_ALT(pin, 2); /* set mode to ALT 0 */
|
||||
+ }
|
||||
+#undef INP_GPIO
|
||||
+#undef SET_GPIO_ALT
|
||||
+}
|
||||
+
|
||||
+static const struct snd_pcm_hardware bcm2708_pcm_hardware = {
|
||||
+ .info = SNDRV_PCM_INFO_MMAP |
|
||||
+ SNDRV_PCM_INFO_MMAP_VALID |
|
||||
+ SNDRV_PCM_INFO_INTERLEAVED |
|
||||
+ SNDRV_PCM_INFO_JOINT_DUPLEX,
|
||||
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
|
||||
+ SNDRV_PCM_FMTBIT_S32_LE,
|
||||
+ .period_bytes_min = 32,
|
||||
+ .period_bytes_max = 64 * PAGE_SIZE,
|
||||
+ .periods_min = 2,
|
||||
+ .periods_max = 255,
|
||||
+ .buffer_bytes_max = 128 * PAGE_SIZE,
|
||||
+};
|
||||
+
|
||||
+static const struct snd_dmaengine_pcm_config bcm2708_dmaengine_pcm_config = {
|
||||
+ .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
|
||||
+ .pcm_hardware = &bcm2708_pcm_hardware,
|
||||
+ .prealloc_buffer_size = 256 * PAGE_SIZE,
|
||||
+};
|
||||
+
|
||||
+
|
||||
+static int bcm2708_i2s_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct bcm2708_i2s_dev *dev;
|
||||
+ int i;
|
||||
+ int ret;
|
||||
+ struct regmap *regmap[2];
|
||||
+ struct resource *mem[2];
|
||||
+
|
||||
+ /* Request both ioareas */
|
||||
+ for (i = 0; i <= 1; i++) {
|
||||
+ void __iomem *base;
|
||||
+
|
||||
+ mem[i] = platform_get_resource(pdev, IORESOURCE_MEM, i);
|
||||
+ base = devm_ioremap_resource(&pdev->dev, mem[i]);
|
||||
+ if (IS_ERR(base))
|
||||
+ return PTR_ERR(base);
|
||||
+
|
||||
+ regmap[i] = devm_regmap_init_mmio(&pdev->dev, base,
|
||||
+ &bcm2708_regmap_config[i]);
|
||||
+ if (IS_ERR(regmap[i])) {
|
||||
+ dev_err(&pdev->dev, "I2S probe: regmap init failed\n");
|
||||
+ return PTR_ERR(regmap[i]);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev),
|
||||
+ GFP_KERNEL);
|
||||
+ if (IS_ERR(dev))
|
||||
+ return PTR_ERR(dev);
|
||||
+
|
||||
+ bcm2708_i2s_setup_gpio();
|
||||
+
|
||||
+ dev->i2s_regmap = regmap[0];
|
||||
+ dev->clk_regmap = regmap[1];
|
||||
+
|
||||
+ /* Set the DMA address */
|
||||
+ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr =
|
||||
+ (dma_addr_t)BCM2708_I2S_FIFO_PHYSICAL_ADDR;
|
||||
+
|
||||
+ dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr =
|
||||
+ (dma_addr_t)BCM2708_I2S_FIFO_PHYSICAL_ADDR;
|
||||
+
|
||||
+ /* Set the DREQ */
|
||||
+ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].slave_id =
|
||||
+ BCM2708_DMA_DREQ_PCM_TX;
|
||||
+ dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].slave_id =
|
||||
+ BCM2708_DMA_DREQ_PCM_RX;
|
||||
+
|
||||
+ /* Set the bus width */
|
||||
+ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr_width =
|
||||
+ DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
+ dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr_width =
|
||||
+ DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
+
|
||||
+ /* Set burst */
|
||||
+ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].maxburst = 2;
|
||||
+ dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].maxburst = 2;
|
||||
+
|
||||
+ /* BCLK ratio - use default */
|
||||
+ dev->bclk_ratio = 0;
|
||||
+
|
||||
+ /* Store the pdev */
|
||||
+ dev->dev = &pdev->dev;
|
||||
+ dev_set_drvdata(&pdev->dev, dev);
|
||||
+
|
||||
+ ret = snd_soc_register_component(&pdev->dev,
|
||||
+ &bcm2708_i2s_component, &bcm2708_i2s_dai, 1);
|
||||
+
|
||||
+ if (ret) {
|
||||
+ dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
|
||||
+ ret = -ENOMEM;
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ ret = snd_dmaengine_pcm_register(&pdev->dev,
|
||||
+ &bcm2708_dmaengine_pcm_config,
|
||||
+ SND_DMAENGINE_PCM_FLAG_COMPAT);
|
||||
+ if (ret) {
|
||||
+ dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
|
||||
+ snd_soc_unregister_component(&pdev->dev);
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int bcm2708_i2s_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ snd_dmaengine_pcm_unregister(&pdev->dev);
|
||||
+ snd_soc_unregister_component(&pdev->dev);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static struct platform_driver bcm2708_i2s_driver = {
|
||||
+ .probe = bcm2708_i2s_probe,
|
||||
+ .remove = bcm2708_i2s_remove,
|
||||
+ .driver = {
|
||||
+ .name = "bcm2708-i2s",
|
||||
+ .owner = THIS_MODULE,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+module_platform_driver(bcm2708_i2s_driver);
|
||||
+
|
||||
+MODULE_ALIAS("platform:bcm2708-i2s");
|
||||
+MODULE_DESCRIPTION("BCM2708 I2S interface");
|
||||
+MODULE_AUTHOR("Florian Meier <florian.meier@koalo.de>");
|
||||
+MODULE_LICENSE("GPL v2");
|
|
@ -1,40 +0,0 @@
|
|||
From 824d59b5066dff20761ecedebd4fbce27f301c70 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Meier <florian.meier@koalo.de>
|
||||
Date: Fri, 22 Nov 2013 14:37:51 +0100
|
||||
Subject: [PATCH 35/54] BCM2708: Extend mach header
|
||||
|
||||
Extend the headers of the mach-bcm2708
|
||||
in order to support I2S and DMA engine.
|
||||
|
||||
Signed-off-by: Florian Meier <florian.meier@koalo.de>
|
||||
---
|
||||
arch/arm/mach-bcm2708/include/mach/dma.h | 2 ++
|
||||
arch/arm/mach-bcm2708/include/mach/platform.h | 2 ++
|
||||
2 files changed, 4 insertions(+)
|
||||
|
||||
--- a/arch/arm/mach-bcm2708/include/mach/dma.h
|
||||
+++ b/arch/arm/mach-bcm2708/include/mach/dma.h
|
||||
@@ -45,6 +45,8 @@
|
||||
#define BCM2708_DMA_ADDR 0x04
|
||||
/* the current control block appears in the following registers - read only */
|
||||
#define BCM2708_DMA_INFO 0x08
|
||||
+#define BCM2708_DMA_SOURCE_AD 0x0c
|
||||
+#define BCM2708_DMA_DEST_AD 0x10
|
||||
#define BCM2708_DMA_NEXTCB 0x1C
|
||||
#define BCM2708_DMA_DEBUG 0x20
|
||||
|
||||
--- a/arch/arm/mach-bcm2708/include/mach/platform.h
|
||||
+++ b/arch/arm/mach-bcm2708/include/mach/platform.h
|
||||
@@ -62,10 +62,12 @@
|
||||
#define DMA_BASE (BCM2708_PERI_BASE + 0x7000) /* DMA controller */
|
||||
#define ARM_BASE (BCM2708_PERI_BASE + 0xB000) /* BCM2708 ARM control block */
|
||||
#define PM_BASE (BCM2708_PERI_BASE + 0x100000) /* Power Management, Reset controller and Watchdog registers */
|
||||
+#define PCM_CLOCK_BASE (BCM2708_PERI_BASE + 0x101098) /* PCM Clock */
|
||||
#define RNG_BASE (BCM2708_PERI_BASE + 0x104000) /* Hardware RNG */
|
||||
#define GPIO_BASE (BCM2708_PERI_BASE + 0x200000) /* GPIO */
|
||||
#define UART0_BASE (BCM2708_PERI_BASE + 0x201000) /* Uart 0 */
|
||||
#define MMCI0_BASE (BCM2708_PERI_BASE + 0x202000) /* MMC interface */
|
||||
+#define I2S_BASE (BCM2708_PERI_BASE + 0x203000) /* I2S */
|
||||
#define SPI0_BASE (BCM2708_PERI_BASE + 0x204000) /* SPI0 */
|
||||
#define BSC0_BASE (BCM2708_PERI_BASE + 0x205000) /* BSC0 I2C/TWI */
|
||||
#define UART1_BASE (BCM2708_PERI_BASE + 0x215000) /* Uart 1 */
|
|
@ -1,120 +0,0 @@
|
|||
From c2155e69578ea9f02c915960599bb5216ce860c1 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Meier <florian.meier@koalo.de>
|
||||
Date: Fri, 22 Nov 2013 14:59:51 +0100
|
||||
Subject: [PATCH 36/54] ASoC: Add support for PCM5102A codec
|
||||
|
||||
Some definitions to support the PCM5102A codec
|
||||
by Texas Instruments.
|
||||
|
||||
Signed-off-by: Florian Meier <florian.meier@koalo.de>
|
||||
---
|
||||
sound/soc/codecs/Kconfig | 4 +++
|
||||
sound/soc/codecs/Makefile | 2 ++
|
||||
sound/soc/codecs/pcm5102a.c | 63 +++++++++++++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 69 insertions(+)
|
||||
create mode 100644 sound/soc/codecs/pcm5102a.c
|
||||
|
||||
--- a/sound/soc/codecs/Kconfig
|
||||
+++ b/sound/soc/codecs/Kconfig
|
||||
@@ -59,6 +59,7 @@ config SND_SOC_ALL_CODECS
|
||||
select SND_SOC_PCM1681 if I2C
|
||||
select SND_SOC_PCM1792A if SPI_MASTER
|
||||
select SND_SOC_PCM3008
|
||||
+ select SND_SOC_PCM5102A
|
||||
select SND_SOC_RT5631 if I2C
|
||||
select SND_SOC_RT5640 if I2C
|
||||
select SND_SOC_SGTL5000 if I2C
|
||||
@@ -313,6 +314,9 @@ config SND_SOC_PCM1792A
|
||||
config SND_SOC_PCM3008
|
||||
tristate
|
||||
|
||||
+config SND_SOC_PCM5102A
|
||||
+ tristate
|
||||
+
|
||||
config SND_SOC_RT5631
|
||||
tristate
|
||||
|
||||
--- a/sound/soc/codecs/Makefile
|
||||
+++ b/sound/soc/codecs/Makefile
|
||||
@@ -46,6 +46,7 @@ snd-soc-hdmi-codec-objs := hdmi.o
|
||||
snd-soc-pcm1681-objs := pcm1681.o
|
||||
snd-soc-pcm1792a-codec-objs := pcm1792a.o
|
||||
snd-soc-pcm3008-objs := pcm3008.o
|
||||
+snd-soc-pcm5102a-objs := pcm5102a.o
|
||||
snd-soc-rt5631-objs := rt5631.o
|
||||
snd-soc-rt5640-objs := rt5640.o
|
||||
snd-soc-sgtl5000-objs := sgtl5000.o
|
||||
@@ -179,6 +180,7 @@ obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-
|
||||
obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o
|
||||
obj-$(CONFIG_SND_SOC_PCM1792A) += snd-soc-pcm1792a-codec.o
|
||||
obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
|
||||
+obj-$(CONFIG_SND_SOC_PCM5102A) += snd-soc-pcm5102a.o
|
||||
obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o
|
||||
obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o
|
||||
obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o
|
||||
--- /dev/null
|
||||
+++ b/sound/soc/codecs/pcm5102a.c
|
||||
@@ -0,0 +1,63 @@
|
||||
+/*
|
||||
+ * Driver for the PCM5102A codec
|
||||
+ *
|
||||
+ * Author: Florian Meier <florian.meier@koalo.de>
|
||||
+ * Copyright 2013
|
||||
+ *
|
||||
+ * 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/init.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+
|
||||
+#include <sound/soc.h>
|
||||
+
|
||||
+static struct snd_soc_dai_driver pcm5102a_dai = {
|
||||
+ .name = "pcm5102a-hifi",
|
||||
+ .playback = {
|
||||
+ .channels_min = 2,
|
||||
+ .channels_max = 2,
|
||||
+ .rates = SNDRV_PCM_RATE_8000_192000,
|
||||
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
|
||||
+ SNDRV_PCM_FMTBIT_S24_LE |
|
||||
+ SNDRV_PCM_FMTBIT_S32_LE
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+static struct snd_soc_codec_driver soc_codec_dev_pcm5102a;
|
||||
+
|
||||
+static int pcm5102a_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_pcm5102a,
|
||||
+ &pcm5102a_dai, 1);
|
||||
+}
|
||||
+
|
||||
+static int pcm5102a_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ snd_soc_unregister_codec(&pdev->dev);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static struct platform_driver pcm5102a_codec_driver = {
|
||||
+ .probe = pcm5102a_probe,
|
||||
+ .remove = pcm5102a_remove,
|
||||
+ .driver = {
|
||||
+ .name = "pcm5102a-codec",
|
||||
+ .owner = THIS_MODULE,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+module_platform_driver(pcm5102a_codec_driver);
|
||||
+
|
||||
+MODULE_DESCRIPTION("ASoC PCM5102A codec driver");
|
||||
+MODULE_AUTHOR("Florian Meier <florian.meier@koalo.de>");
|
||||
+MODULE_LICENSE("GPL v2");
|
|
@ -1,55 +0,0 @@
|
|||
From 575806f9f3c40e16ac377c9d7d076fb87773968a Mon Sep 17 00:00:00 2001
|
||||
From: Florian Meier <florian.meier@koalo.de>
|
||||
Date: Fri, 22 Nov 2013 19:04:54 +0100
|
||||
Subject: [PATCH 37/54] BCM2708: Add I2S support to board file
|
||||
|
||||
Adds the required initializations for I2S
|
||||
to the board file of mach-bcm2708.
|
||||
|
||||
Signed-off-by: Florian Meier <florian.meier@koalo.de>
|
||||
---
|
||||
arch/arm/mach-bcm2708/bcm2708.c | 26 ++++++++++++++++++++++++++
|
||||
1 file changed, 26 insertions(+)
|
||||
|
||||
--- a/arch/arm/mach-bcm2708/bcm2708.c
|
||||
+++ b/arch/arm/mach-bcm2708/bcm2708.c
|
||||
@@ -615,6 +615,28 @@ static struct platform_device bcm2835_th
|
||||
.name = "bcm2835_thermal",
|
||||
};
|
||||
|
||||
+#ifdef CONFIG_SND_BCM2708_SOC_I2S_MODULE
|
||||
+static struct resource bcm2708_i2s_resources[] = {
|
||||
+ {
|
||||
+ .start = I2S_BASE,
|
||||
+ .end = I2S_BASE + 0x20,
|
||||
+ .flags = IORESOURCE_MEM,
|
||||
+ },
|
||||
+ {
|
||||
+ .start = PCM_CLOCK_BASE,
|
||||
+ .end = PCM_CLOCK_BASE + 0x02,
|
||||
+ .flags = IORESOURCE_MEM,
|
||||
+ }
|
||||
+};
|
||||
+
|
||||
+static struct platform_device bcm2708_i2s_device = {
|
||||
+ .name = "bcm2708-i2s",
|
||||
+ .id = 0,
|
||||
+ .num_resources = ARRAY_SIZE(bcm2708_i2s_resources),
|
||||
+ .resource = bcm2708_i2s_resources,
|
||||
+};
|
||||
+#endif
|
||||
+
|
||||
int __init bcm_register_device(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
@@ -738,6 +760,10 @@ void __init bcm2708_init(void)
|
||||
bcm_register_device(&bcm2835_hwmon_device);
|
||||
bcm_register_device(&bcm2835_thermal_device);
|
||||
|
||||
+#ifdef CONFIG_SND_BCM2708_SOC_I2S_MODULE
|
||||
+ bcm_register_device(&bcm2708_i2s_device);
|
||||
+#endif
|
||||
+
|
||||
for (i = 0; i < ARRAY_SIZE(amba_devs); i++) {
|
||||
struct amba_device *d = amba_devs[i];
|
||||
amba_device_register(d, &iomem_resource);
|
|
@ -1,144 +0,0 @@
|
|||
From 674e7cb3c8707e66cabe7adffca58fd504c451d0 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Meier <florian.meier@koalo.de>
|
||||
Date: Fri, 22 Nov 2013 19:19:08 +0100
|
||||
Subject: [PATCH 38/54] ASoC: Add support for HifiBerry DAC
|
||||
|
||||
This adds a machine driver for the HifiBerry DAC.
|
||||
It is a sound card that can
|
||||
be stacked onto the Raspberry Pi.
|
||||
|
||||
Signed-off-by: Florian Meier <florian.meier@koalo.de>
|
||||
---
|
||||
sound/soc/bcm/Kconfig | 7 +++
|
||||
sound/soc/bcm/Makefile | 5 +++
|
||||
sound/soc/bcm/hifiberry_dac.c | 100 ++++++++++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 112 insertions(+)
|
||||
create mode 100644 sound/soc/bcm/hifiberry_dac.c
|
||||
|
||||
--- a/sound/soc/bcm/Kconfig
|
||||
+++ b/sound/soc/bcm/Kconfig
|
||||
@@ -18,3 +18,10 @@ config SND_BCM2708_SOC_I2S
|
||||
Say Y or M if you want to add support for codecs attached to
|
||||
the BCM2708 I2S interface. You will also need
|
||||
to select the audio interfaces to support below.
|
||||
+
|
||||
+config SND_BCM2708_SOC_HIFIBERRY_DAC
|
||||
+ tristate "Support for HifiBerry DAC"
|
||||
+ depends on SND_BCM2708_SOC_I2S
|
||||
+ select SND_SOC_PCM5102A
|
||||
+ help
|
||||
+ Say Y or M if you want to add support for HifiBerry DAC.
|
||||
--- a/sound/soc/bcm/Makefile
|
||||
+++ b/sound/soc/bcm/Makefile
|
||||
@@ -7,3 +7,8 @@ obj-$(CONFIG_SND_BCM2835_SOC_I2S) += snd
|
||||
snd-soc-bcm2708-i2s-objs := bcm2708-i2s.o
|
||||
|
||||
obj-$(CONFIG_SND_BCM2708_SOC_I2S) += snd-soc-bcm2708-i2s.o
|
||||
+
|
||||
+# BCM2708 Machine Support
|
||||
+snd-soc-hifiberry-dac-objs := hifiberry_dac.o
|
||||
+
|
||||
+obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) += snd-soc-hifiberry-dac.o
|
||||
--- /dev/null
|
||||
+++ b/sound/soc/bcm/hifiberry_dac.c
|
||||
@@ -0,0 +1,100 @@
|
||||
+/*
|
||||
+ * ASoC Driver for HifiBerry DAC
|
||||
+ *
|
||||
+ * Author: Florian Meier <florian.meier@koalo.de>
|
||||
+ * Copyright 2013
|
||||
+ *
|
||||
+ * 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 <sound/core.h>
|
||||
+#include <sound/pcm.h>
|
||||
+#include <sound/pcm_params.h>
|
||||
+#include <sound/soc.h>
|
||||
+#include <sound/jack.h>
|
||||
+
|
||||
+static int snd_rpi_hifiberry_dac_init(struct snd_soc_pcm_runtime *rtd)
|
||||
+{
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int snd_rpi_hifiberry_dac_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 *cpu_dai = rtd->cpu_dai;
|
||||
+
|
||||
+ unsigned int sample_bits =
|
||||
+ snd_pcm_format_physical_width(params_format(params));
|
||||
+
|
||||
+ return snd_soc_dai_set_bclk_ratio(cpu_dai, sample_bits * 2);
|
||||
+}
|
||||
+
|
||||
+/* machine stream operations */
|
||||
+static struct snd_soc_ops snd_rpi_hifiberry_dac_ops = {
|
||||
+ .hw_params = snd_rpi_hifiberry_dac_hw_params,
|
||||
+};
|
||||
+
|
||||
+static struct snd_soc_dai_link snd_rpi_hifiberry_dac_dai[] = {
|
||||
+{
|
||||
+ .name = "HifiBerry DAC",
|
||||
+ .stream_name = "HifiBerry DAC HiFi",
|
||||
+ .cpu_dai_name = "bcm2708-i2s.0",
|
||||
+ .codec_dai_name = "pcm5102a-hifi",
|
||||
+ .platform_name = "bcm2708-i2s.0",
|
||||
+ .codec_name = "pcm5102a-codec",
|
||||
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
|
||||
+ SND_SOC_DAIFMT_CBS_CFS,
|
||||
+ .ops = &snd_rpi_hifiberry_dac_ops,
|
||||
+ .init = snd_rpi_hifiberry_dac_init,
|
||||
+},
|
||||
+};
|
||||
+
|
||||
+/* audio machine driver */
|
||||
+static struct snd_soc_card snd_rpi_hifiberry_dac = {
|
||||
+ .name = "snd_rpi_hifiberry_dac",
|
||||
+ .dai_link = snd_rpi_hifiberry_dac_dai,
|
||||
+ .num_links = ARRAY_SIZE(snd_rpi_hifiberry_dac_dai),
|
||||
+};
|
||||
+
|
||||
+static int snd_rpi_hifiberry_dac_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ snd_rpi_hifiberry_dac.dev = &pdev->dev;
|
||||
+ ret = snd_soc_register_card(&snd_rpi_hifiberry_dac);
|
||||
+ if (ret)
|
||||
+ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int snd_rpi_hifiberry_dac_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ return snd_soc_unregister_card(&snd_rpi_hifiberry_dac);
|
||||
+}
|
||||
+
|
||||
+static struct platform_driver snd_rpi_hifiberry_dac_driver = {
|
||||
+ .driver = {
|
||||
+ .name = "snd-hifiberry-dac",
|
||||
+ .owner = THIS_MODULE,
|
||||
+ },
|
||||
+ .probe = snd_rpi_hifiberry_dac_probe,
|
||||
+ .remove = snd_rpi_hifiberry_dac_remove,
|
||||
+};
|
||||
+
|
||||
+module_platform_driver(snd_rpi_hifiberry_dac_driver);
|
||||
+
|
||||
+MODULE_AUTHOR("Florian Meier <florian.meier@koalo.de>");
|
||||
+MODULE_DESCRIPTION("ASoC Driver for HifiBerry DAC");
|
||||
+MODULE_LICENSE("GPL v2");
|
|
@ -1,48 +0,0 @@
|
|||
From c5749de8927e1b64eaf5e6c7401a24d1ee0fdc68 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Meier <florian.meier@koalo.de>
|
||||
Date: Fri, 22 Nov 2013 19:21:34 +0100
|
||||
Subject: [PATCH 39/54] BCM2708: Add HifiBerry DAC to board file
|
||||
|
||||
This adds the initalization of the HifiBerry DAC
|
||||
to the mach-bcm2708 board file.
|
||||
|
||||
Signed-off-by: Florian Meier <florian.meier@koalo.de>
|
||||
---
|
||||
arch/arm/mach-bcm2708/bcm2708.c | 19 +++++++++++++++++++
|
||||
1 file changed, 19 insertions(+)
|
||||
|
||||
--- a/arch/arm/mach-bcm2708/bcm2708.c
|
||||
+++ b/arch/arm/mach-bcm2708/bcm2708.c
|
||||
@@ -637,6 +637,20 @@ static struct platform_device bcm2708_i2
|
||||
};
|
||||
#endif
|
||||
|
||||
+#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC_MODULE)
|
||||
+static struct platform_device snd_hifiberry_dac_device = {
|
||||
+ .name = "snd-hifiberry-dac",
|
||||
+ .id = 0,
|
||||
+ .num_resources = 0,
|
||||
+};
|
||||
+
|
||||
+static struct platform_device snd_pcm5102a_codec_device = {
|
||||
+ .name = "pcm5102a-codec",
|
||||
+ .id = -1,
|
||||
+ .num_resources = 0,
|
||||
+};
|
||||
+#endif
|
||||
+
|
||||
int __init bcm_register_device(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
@@ -764,6 +778,11 @@ void __init bcm2708_init(void)
|
||||
bcm_register_device(&bcm2708_i2s_device);
|
||||
#endif
|
||||
|
||||
+#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC_MODULE)
|
||||
+ bcm_register_device(&snd_hifiberry_dac_device);
|
||||
+ bcm_register_device(&snd_pcm5102a_codec_device);
|
||||
+#endif
|
||||
+
|
||||
for (i = 0; i < ARRAY_SIZE(amba_devs); i++) {
|
||||
struct amba_device *d = amba_devs[i];
|
||||
amba_device_register(d, &iomem_resource);
|
|
@ -1,72 +0,0 @@
|
|||
From 5b06b2affe65b243dd828aeac9f48854902b1823 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Meier <florian.meier@koalo.de>
|
||||
Date: Fri, 6 Dec 2013 18:55:53 +0100
|
||||
Subject: [PATCH 40/54] ASoC: BCM2708: Add 24 bit support
|
||||
|
||||
This adds 24 bit support to the I2S driver of the BCM2708.
|
||||
Besides enabling the 24 bit flags, it includes two bug fixes:
|
||||
|
||||
MMAP is not supported. Claiming this leads to strange issues
|
||||
when the format of driver and file do not match.
|
||||
|
||||
The datasheet states that the width extension bit should be set
|
||||
for widths greater than 24, but greater or equal would be correct.
|
||||
This follows from the definition of the width field.
|
||||
|
||||
Signed-off-by: Florian Meier <florian.meier@koalo.de>
|
||||
---
|
||||
sound/soc/bcm/bcm2708-i2s.c | 13 +++++++++----
|
||||
1 file changed, 9 insertions(+), 4 deletions(-)
|
||||
|
||||
--- a/sound/soc/bcm/bcm2708-i2s.c
|
||||
+++ b/sound/soc/bcm/bcm2708-i2s.c
|
||||
@@ -346,6 +346,10 @@ static int bcm2708_i2s_hw_params(struct
|
||||
data_length = 16;
|
||||
bclk_ratio = 40;
|
||||
break;
|
||||
+ case SNDRV_PCM_FORMAT_S24_LE:
|
||||
+ data_length = 24;
|
||||
+ bclk_ratio = 40;
|
||||
+ break;
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
data_length = 32;
|
||||
bclk_ratio = 80;
|
||||
@@ -424,7 +428,7 @@ static int bcm2708_i2s_hw_params(struct
|
||||
/* Setup the frame format */
|
||||
format = BCM2708_I2S_CHEN;
|
||||
|
||||
- if (data_length > 24)
|
||||
+ if (data_length >= 24)
|
||||
format |= BCM2708_I2S_CHWEX;
|
||||
|
||||
format |= BCM2708_I2S_CHWID((data_length-8)&0xf);
|
||||
@@ -714,6 +718,7 @@ static struct snd_soc_dai_driver bcm2708
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_8000_192000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE
|
||||
+ | SNDRV_PCM_FMTBIT_S24_LE
|
||||
| SNDRV_PCM_FMTBIT_S32_LE
|
||||
},
|
||||
.capture = {
|
||||
@@ -721,6 +726,7 @@ static struct snd_soc_dai_driver bcm2708
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_8000_192000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE
|
||||
+ | SNDRV_PCM_FMTBIT_S24_LE
|
||||
| SNDRV_PCM_FMTBIT_S32_LE
|
||||
},
|
||||
.ops = &bcm2708_i2s_dai_ops,
|
||||
@@ -810,11 +816,10 @@ static void bcm2708_i2s_setup_gpio(void)
|
||||
}
|
||||
|
||||
static const struct snd_pcm_hardware bcm2708_pcm_hardware = {
|
||||
- .info = SNDRV_PCM_INFO_MMAP |
|
||||
- SNDRV_PCM_INFO_MMAP_VALID |
|
||||
- SNDRV_PCM_INFO_INTERLEAVED |
|
||||
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_JOINT_DUPLEX,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE |
|
||||
+ SNDRV_PCM_FMTBIT_S24_LE |
|
||||
SNDRV_PCM_FMTBIT_S32_LE,
|
||||
.period_bytes_min = 32,
|
||||
.period_bytes_max = 64 * PAGE_SIZE,
|
|
@ -1,40 +0,0 @@
|
|||
From 8dd97bbaf4bd92b07bc942e4012b68f64b8a5ebc Mon Sep 17 00:00:00 2001
|
||||
From: Florian Meier <florian.meier@koalo.de>
|
||||
Date: Mon, 2 Dec 2013 20:28:22 +0100
|
||||
Subject: [PATCH 41/54] BCM2708: Add I2S and DMA support to default config
|
||||
|
||||
This commit adds several modules that are needed for
|
||||
I2S support for the Raspberry Pi to the defconfig.
|
||||
|
||||
Signed-off-by: Florian Meier <florian.meier@koalo.de>
|
||||
---
|
||||
arch/arm/configs/bcmrpi_defconfig | 11 +++++++++++
|
||||
1 file changed, 11 insertions(+)
|
||||
|
||||
--- a/arch/arm/configs/bcmrpi_defconfig
|
||||
+++ b/arch/arm/configs/bcmrpi_defconfig
|
||||
@@ -739,6 +739,13 @@ CONFIG_SND_USB_UA101=m
|
||||
CONFIG_SND_USB_CAIAQ=m
|
||||
CONFIG_SND_USB_CAIAQ_INPUT=y
|
||||
CONFIG_SND_USB_6FIRE=m
|
||||
+CONFIG_SND_SOC=m
|
||||
+CONFIG_SND_SOC_DMAENGINE_PCM=y
|
||||
+CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM=y
|
||||
+CONFIG_SND_BCM2708_SOC_I2S=m
|
||||
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m
|
||||
+CONFIG_SND_SOC_I2C_AND_SPI=m
|
||||
+CONFIG_SND_SOC_PCM5102A=m
|
||||
CONFIG_SOUND_PRIME=m
|
||||
CONFIG_HIDRAW=y
|
||||
CONFIG_HID_A4TECH=m
|
||||
@@ -929,6 +936,10 @@ CONFIG_RTC_DRV_RS5C348=m
|
||||
CONFIG_RTC_DRV_DS3234=m
|
||||
CONFIG_RTC_DRV_PCF2123=m
|
||||
CONFIG_RTC_DRV_RX4581=m
|
||||
+CONFIG_DMADEVICES=y
|
||||
+CONFIG_DMA_BCM2708=m
|
||||
+CONFIG_DMA_ENGINE=y
|
||||
+CONFIG_DMA_VIRTUAL_CHANNELS=m
|
||||
CONFIG_UIO=m
|
||||
CONFIG_UIO_PDRV_GENIRQ=m
|
||||
CONFIG_STAGING=y
|
|
@ -1,295 +0,0 @@
|
|||
From ab038bbb25bcd86ef59a5f5651ce00d30190d4ad Mon Sep 17 00:00:00 2001
|
||||
From: Florian Meier <florian.meier@koalo.de>
|
||||
Date: Fri, 6 Dec 2013 20:50:28 +0100
|
||||
Subject: [PATCH 42/54] ASoC: BCM2708: Add support for RPi-DAC
|
||||
|
||||
This adds a machine driver for the RPi-DAC.
|
||||
|
||||
Signed-off-by: Florian Meier <florian.meier@koalo.de>
|
||||
---
|
||||
arch/arm/configs/bcmrpi_defconfig | 2 +
|
||||
arch/arm/mach-bcm2708/bcm2708.c | 19 ++++++++
|
||||
sound/soc/bcm/Kconfig | 7 +++
|
||||
sound/soc/bcm/Makefile | 2 +
|
||||
sound/soc/bcm/rpi-dac.c | 97 +++++++++++++++++++++++++++++++++++++++
|
||||
sound/soc/codecs/Kconfig | 4 ++
|
||||
sound/soc/codecs/Makefile | 2 +
|
||||
sound/soc/codecs/pcm1794a.c | 62 +++++++++++++++++++++++++
|
||||
8 files changed, 195 insertions(+)
|
||||
create mode 100644 sound/soc/bcm/rpi-dac.c
|
||||
create mode 100644 sound/soc/codecs/pcm1794a.c
|
||||
|
||||
--- a/arch/arm/configs/bcmrpi_defconfig
|
||||
+++ b/arch/arm/configs/bcmrpi_defconfig
|
||||
@@ -744,8 +744,10 @@ CONFIG_SND_SOC_DMAENGINE_PCM=y
|
||||
CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM=y
|
||||
CONFIG_SND_BCM2708_SOC_I2S=m
|
||||
CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m
|
||||
+CONFIG_SND_BCM2708_SOC_RPI_DAC=m
|
||||
CONFIG_SND_SOC_I2C_AND_SPI=m
|
||||
CONFIG_SND_SOC_PCM5102A=m
|
||||
+CONFIG_SND_SOC_PCM1794A=m
|
||||
CONFIG_SOUND_PRIME=m
|
||||
CONFIG_HIDRAW=y
|
||||
CONFIG_HID_A4TECH=m
|
||||
--- a/arch/arm/mach-bcm2708/bcm2708.c
|
||||
+++ b/arch/arm/mach-bcm2708/bcm2708.c
|
||||
@@ -651,6 +651,20 @@ static struct platform_device snd_pcm510
|
||||
};
|
||||
#endif
|
||||
|
||||
+#if defined(CONFIG_SND_BCM2708_SOC_RPI_DAC) || defined(CONFIG_SND_BCM2708_SOC_RPI_DAC_MODULE)
|
||||
+static struct platform_device snd_rpi_dac_device = {
|
||||
+ .name = "snd-rpi-dac",
|
||||
+ .id = 0,
|
||||
+ .num_resources = 0,
|
||||
+};
|
||||
+
|
||||
+static struct platform_device snd_pcm1794a_codec_device = {
|
||||
+ .name = "pcm1794a-codec",
|
||||
+ .id = -1,
|
||||
+ .num_resources = 0,
|
||||
+};
|
||||
+#endif
|
||||
+
|
||||
int __init bcm_register_device(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
@@ -783,6 +797,11 @@ void __init bcm2708_init(void)
|
||||
bcm_register_device(&snd_pcm5102a_codec_device);
|
||||
#endif
|
||||
|
||||
+#if defined(CONFIG_SND_BCM2708_SOC_RPI_DAC) || defined(CONFIG_SND_BCM2708_SOC_RPI_DAC_MODULE)
|
||||
+ bcm_register_device(&snd_rpi_dac_device);
|
||||
+ bcm_register_device(&snd_pcm1794a_codec_device);
|
||||
+#endif
|
||||
+
|
||||
for (i = 0; i < ARRAY_SIZE(amba_devs); i++) {
|
||||
struct amba_device *d = amba_devs[i];
|
||||
amba_device_register(d, &iomem_resource);
|
||||
--- a/sound/soc/bcm/Kconfig
|
||||
+++ b/sound/soc/bcm/Kconfig
|
||||
@@ -25,3 +25,10 @@ config SND_BCM2708_SOC_HIFIBERRY_DAC
|
||||
select SND_SOC_PCM5102A
|
||||
help
|
||||
Say Y or M if you want to add support for HifiBerry DAC.
|
||||
+
|
||||
+config SND_BCM2708_SOC_RPI_DAC
|
||||
+ tristate "Support for RPi-DAC"
|
||||
+ depends on SND_BCM2708_SOC_I2S
|
||||
+ select SND_SOC_PCM1794A
|
||||
+ help
|
||||
+ Say Y or M if you want to add support for RPi-DAC.
|
||||
--- a/sound/soc/bcm/Makefile
|
||||
+++ b/sound/soc/bcm/Makefile
|
||||
@@ -10,5 +10,7 @@ obj-$(CONFIG_SND_BCM2708_SOC_I2S) += snd
|
||||
|
||||
# BCM2708 Machine Support
|
||||
snd-soc-hifiberry-dac-objs := hifiberry_dac.o
|
||||
+snd-soc-rpi-dac-objs := rpi-dac.o
|
||||
|
||||
obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) += snd-soc-hifiberry-dac.o
|
||||
+obj-$(CONFIG_SND_BCM2708_SOC_RPI_DAC) += snd-soc-rpi-dac.o
|
||||
--- /dev/null
|
||||
+++ b/sound/soc/bcm/rpi-dac.c
|
||||
@@ -0,0 +1,97 @@
|
||||
+/*
|
||||
+ * ASoC Driver for RPi-DAC.
|
||||
+ *
|
||||
+ * Author: Florian Meier <florian.meier@koalo.de>
|
||||
+ * Copyright 2013
|
||||
+ *
|
||||
+ * 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 <sound/core.h>
|
||||
+#include <sound/pcm.h>
|
||||
+#include <sound/pcm_params.h>
|
||||
+#include <sound/soc.h>
|
||||
+#include <sound/jack.h>
|
||||
+
|
||||
+static int snd_rpi_rpi_dac_init(struct snd_soc_pcm_runtime *rtd)
|
||||
+{
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int snd_rpi_rpi_dac_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 *cpu_dai = rtd->cpu_dai;
|
||||
+
|
||||
+ return snd_soc_dai_set_bclk_ratio(cpu_dai, 32*2);
|
||||
+}
|
||||
+
|
||||
+/* machine stream operations */
|
||||
+static struct snd_soc_ops snd_rpi_rpi_dac_ops = {
|
||||
+ .hw_params = snd_rpi_rpi_dac_hw_params,
|
||||
+};
|
||||
+
|
||||
+static struct snd_soc_dai_link snd_rpi_rpi_dac_dai[] = {
|
||||
+{
|
||||
+ .name = "HifiBerry Mini",
|
||||
+ .stream_name = "HifiBerry Mini HiFi",
|
||||
+ .cpu_dai_name = "bcm2708-i2s.0",
|
||||
+ .codec_dai_name = "pcm1794a-hifi",
|
||||
+ .platform_name = "bcm2708-i2s.0",
|
||||
+ .codec_name = "pcm1794a-codec",
|
||||
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
|
||||
+ SND_SOC_DAIFMT_CBS_CFS,
|
||||
+ .ops = &snd_rpi_rpi_dac_ops,
|
||||
+ .init = snd_rpi_rpi_dac_init,
|
||||
+},
|
||||
+};
|
||||
+
|
||||
+/* audio machine driver */
|
||||
+static struct snd_soc_card snd_rpi_rpi_dac = {
|
||||
+ .name = "snd_rpi_rpi_dac",
|
||||
+ .dai_link = snd_rpi_rpi_dac_dai,
|
||||
+ .num_links = ARRAY_SIZE(snd_rpi_rpi_dac_dai),
|
||||
+};
|
||||
+
|
||||
+static int snd_rpi_rpi_dac_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ snd_rpi_rpi_dac.dev = &pdev->dev;
|
||||
+ ret = snd_soc_register_card(&snd_rpi_rpi_dac);
|
||||
+ if (ret)
|
||||
+ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int snd_rpi_rpi_dac_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ return snd_soc_unregister_card(&snd_rpi_rpi_dac);
|
||||
+}
|
||||
+
|
||||
+static struct platform_driver snd_rpi_rpi_dac_driver = {
|
||||
+ .driver = {
|
||||
+ .name = "snd-rpi-dac",
|
||||
+ .owner = THIS_MODULE,
|
||||
+ },
|
||||
+ .probe = snd_rpi_rpi_dac_probe,
|
||||
+ .remove = snd_rpi_rpi_dac_remove,
|
||||
+};
|
||||
+
|
||||
+module_platform_driver(snd_rpi_rpi_dac_driver);
|
||||
+
|
||||
+MODULE_AUTHOR("Florian Meier <florian.meier@koalo.de>");
|
||||
+MODULE_DESCRIPTION("ASoC Driver for RPi-DAC");
|
||||
+MODULE_LICENSE("GPL v2");
|
||||
--- a/sound/soc/codecs/Kconfig
|
||||
+++ b/sound/soc/codecs/Kconfig
|
||||
@@ -59,6 +59,7 @@ config SND_SOC_ALL_CODECS
|
||||
select SND_SOC_PCM1681 if I2C
|
||||
select SND_SOC_PCM1792A if SPI_MASTER
|
||||
select SND_SOC_PCM3008
|
||||
+ select SND_SOC_PCM1794A
|
||||
select SND_SOC_PCM5102A
|
||||
select SND_SOC_RT5631 if I2C
|
||||
select SND_SOC_RT5640 if I2C
|
||||
@@ -314,6 +315,9 @@ config SND_SOC_PCM1792A
|
||||
config SND_SOC_PCM3008
|
||||
tristate
|
||||
|
||||
+config SND_SOC_PCM1794A
|
||||
+ tristate
|
||||
+
|
||||
config SND_SOC_PCM5102A
|
||||
tristate
|
||||
|
||||
--- a/sound/soc/codecs/Makefile
|
||||
+++ b/sound/soc/codecs/Makefile
|
||||
@@ -46,6 +46,7 @@ snd-soc-hdmi-codec-objs := hdmi.o
|
||||
snd-soc-pcm1681-objs := pcm1681.o
|
||||
snd-soc-pcm1792a-codec-objs := pcm1792a.o
|
||||
snd-soc-pcm3008-objs := pcm3008.o
|
||||
+snd-soc-pcm1794a-objs := pcm1794a.o
|
||||
snd-soc-pcm5102a-objs := pcm5102a.o
|
||||
snd-soc-rt5631-objs := rt5631.o
|
||||
snd-soc-rt5640-objs := rt5640.o
|
||||
@@ -180,6 +181,7 @@ obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-
|
||||
obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o
|
||||
obj-$(CONFIG_SND_SOC_PCM1792A) += snd-soc-pcm1792a-codec.o
|
||||
obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
|
||||
+obj-$(CONFIG_SND_SOC_PCM1794A) += snd-soc-pcm1794a.o
|
||||
obj-$(CONFIG_SND_SOC_PCM5102A) += snd-soc-pcm5102a.o
|
||||
obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o
|
||||
obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o
|
||||
--- /dev/null
|
||||
+++ b/sound/soc/codecs/pcm1794a.c
|
||||
@@ -0,0 +1,62 @@
|
||||
+/*
|
||||
+ * Driver for the PCM1794A codec
|
||||
+ *
|
||||
+ * Author: Florian Meier <florian.meier@koalo.de>
|
||||
+ * Copyright 2013
|
||||
+ *
|
||||
+ * 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/init.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+
|
||||
+#include <sound/soc.h>
|
||||
+
|
||||
+static struct snd_soc_dai_driver pcm1794a_dai = {
|
||||
+ .name = "pcm1794a-hifi",
|
||||
+ .playback = {
|
||||
+ .channels_min = 2,
|
||||
+ .channels_max = 2,
|
||||
+ .rates = SNDRV_PCM_RATE_8000_192000,
|
||||
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
|
||||
+ SNDRV_PCM_FMTBIT_S24_LE
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+static struct snd_soc_codec_driver soc_codec_dev_pcm1794a;
|
||||
+
|
||||
+static int pcm1794a_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_pcm1794a,
|
||||
+ &pcm1794a_dai, 1);
|
||||
+}
|
||||
+
|
||||
+static int pcm1794a_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ snd_soc_unregister_codec(&pdev->dev);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static struct platform_driver pcm1794a_codec_driver = {
|
||||
+ .probe = pcm1794a_probe,
|
||||
+ .remove = pcm1794a_remove,
|
||||
+ .driver = {
|
||||
+ .name = "pcm1794a-codec",
|
||||
+ .owner = THIS_MODULE,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+module_platform_driver(pcm1794a_codec_driver);
|
||||
+
|
||||
+MODULE_DESCRIPTION("ASoC PCM1794A codec driver");
|
||||
+MODULE_AUTHOR("Florian Meier <florian.meier@koalo.de>");
|
||||
+MODULE_LICENSE("GPL v2");
|
|
@ -1,112 +0,0 @@
|
|||
From 0b1d46fef20d096cc3991df89ec793a10df0eaf2 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Matuschek <info@crazy-audio.com>
|
||||
Date: Wed, 15 Jan 2014 21:41:23 +0100
|
||||
Subject: [PATCH 43/54] ASoC: wm8804: Implement MCLK configuration options, add
|
||||
32bit support WM8804 can run with PLL frequencies of 256xfs and 128xfs for
|
||||
most sample rates. At 192kHz only 128xfs is supported. The existing driver
|
||||
selects 128xfs automatically for some lower samples rates. By using an
|
||||
additional mclk_div divider, it is now possible to control the behaviour.
|
||||
This allows using 256xfs PLL frequency on all sample rates up to 96kHz. It
|
||||
should allow lower jitter and better signal quality. The behavior has to be
|
||||
controlled by the sound card driver, because some sample frequency share the
|
||||
same setting. e.g. 192kHz and 96kHz use 24.576MHz master clock. The only
|
||||
difference is the MCLK divider.
|
||||
|
||||
This also added support for 32bit data.
|
||||
|
||||
Signed-off-by: Daniel Matuschek <daniel@matuschek.net>
|
||||
---
|
||||
sound/soc/codecs/wm8804.c | 19 +++++++++++++++----
|
||||
sound/soc/codecs/wm8804.h | 4 ++++
|
||||
2 files changed, 19 insertions(+), 4 deletions(-)
|
||||
|
||||
--- a/sound/soc/codecs/wm8804.c
|
||||
+++ b/sound/soc/codecs/wm8804.c
|
||||
@@ -63,6 +63,7 @@ struct wm8804_priv {
|
||||
struct regmap *regmap;
|
||||
struct regulator_bulk_data supplies[WM8804_NUM_SUPPLIES];
|
||||
struct notifier_block disable_nb[WM8804_NUM_SUPPLIES];
|
||||
+ int mclk_div;
|
||||
};
|
||||
|
||||
static int txsrc_get(struct snd_kcontrol *kcontrol,
|
||||
@@ -277,6 +278,7 @@ static int wm8804_hw_params(struct snd_p
|
||||
blen = 0x1;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
+ case SNDRV_PCM_FORMAT_S32_LE:
|
||||
blen = 0x2;
|
||||
break;
|
||||
default:
|
||||
@@ -318,7 +320,7 @@ static struct {
|
||||
|
||||
#define FIXED_PLL_SIZE ((1ULL << 22) * 10)
|
||||
static int pll_factors(struct pll_div *pll_div, unsigned int target,
|
||||
- unsigned int source)
|
||||
+ unsigned int source, unsigned int mclk_div)
|
||||
{
|
||||
u64 Kpart;
|
||||
unsigned long int K, Ndiv, Nmod, tmp;
|
||||
@@ -330,7 +332,8 @@ static int pll_factors(struct pll_div *p
|
||||
*/
|
||||
for (i = 0; i < ARRAY_SIZE(post_table); i++) {
|
||||
tmp = target * post_table[i].div;
|
||||
- if (tmp >= 90000000 && tmp <= 100000000) {
|
||||
+ if ((tmp >= 90000000 && tmp <= 100000000) &&
|
||||
+ (mclk_div == post_table[i].mclkdiv)) {
|
||||
pll_div->freqmode = post_table[i].freqmode;
|
||||
pll_div->mclkdiv = post_table[i].mclkdiv;
|
||||
target *= post_table[i].div;
|
||||
@@ -387,8 +390,11 @@ static int wm8804_set_pll(struct snd_soc
|
||||
} else {
|
||||
int ret;
|
||||
struct pll_div pll_div;
|
||||
+ struct wm8804_priv *wm8804;
|
||||
|
||||
- ret = pll_factors(&pll_div, freq_out, freq_in);
|
||||
+ wm8804 = snd_soc_codec_get_drvdata(codec);
|
||||
+
|
||||
+ ret = pll_factors(&pll_div, freq_out, freq_in, wm8804->mclk_div);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -452,6 +458,7 @@ static int wm8804_set_clkdiv(struct snd_
|
||||
int div_id, int div)
|
||||
{
|
||||
struct snd_soc_codec *codec;
|
||||
+ struct wm8804_priv *wm8804;
|
||||
|
||||
codec = dai->codec;
|
||||
switch (div_id) {
|
||||
@@ -459,6 +466,10 @@ static int wm8804_set_clkdiv(struct snd_
|
||||
snd_soc_update_bits(codec, WM8804_PLL5, 0x30,
|
||||
(div & 0x3) << 4);
|
||||
break;
|
||||
+ case WM8804_MCLK_DIV:
|
||||
+ wm8804 = snd_soc_codec_get_drvdata(codec);
|
||||
+ wm8804->mclk_div = div;
|
||||
+ break;
|
||||
default:
|
||||
dev_err(dai->dev, "Unknown clock divider: %d\n", div_id);
|
||||
return -EINVAL;
|
||||
@@ -641,7 +652,7 @@ static const struct snd_soc_dai_ops wm88
|
||||
};
|
||||
|
||||
#define WM8804_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
|
||||
- SNDRV_PCM_FMTBIT_S24_LE)
|
||||
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
|
||||
|
||||
#define WM8804_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
|
||||
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | \
|
||||
--- a/sound/soc/codecs/wm8804.h
|
||||
+++ b/sound/soc/codecs/wm8804.h
|
||||
@@ -57,5 +57,9 @@
|
||||
#define WM8804_CLKOUT_SRC_OSCCLK 4
|
||||
|
||||
#define WM8804_CLKOUT_DIV 1
|
||||
+#define WM8804_MCLK_DIV 2
|
||||
+
|
||||
+#define WM8804_MCLKDIV_256FS 0
|
||||
+#define WM8804_MCLKDIV_128FS 1
|
||||
|
||||
#endif /* _WM8804_H */
|
|
@ -1,198 +0,0 @@
|
|||
From dad63ece33a339a45c992915eae9d7d8885e98c7 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Matuschek <info@crazy-audio.com>
|
||||
Date: Wed, 15 Jan 2014 21:42:08 +0100
|
||||
Subject: [PATCH 44/54] ASoC: BCM:Add support for HiFiBerry Digi. Driver is
|
||||
based on the patched WM8804 driver.
|
||||
|
||||
Signed-off-by: Daniel Matuschek <daniel@matuschek.net>
|
||||
---
|
||||
sound/soc/bcm/Kconfig | 7 ++
|
||||
sound/soc/bcm/Makefile | 2 +
|
||||
sound/soc/bcm/hifiberry_digi.c | 153 +++++++++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 162 insertions(+)
|
||||
create mode 100644 sound/soc/bcm/hifiberry_digi.c
|
||||
|
||||
--- a/sound/soc/bcm/Kconfig
|
||||
+++ b/sound/soc/bcm/Kconfig
|
||||
@@ -26,6 +26,13 @@ config SND_BCM2708_SOC_HIFIBERRY_DAC
|
||||
help
|
||||
Say Y or M if you want to add support for HifiBerry DAC.
|
||||
|
||||
+config SND_BCM2708_SOC_HIFIBERRY_DIGI
|
||||
+ tristate "Support for HifiBerry Digi"
|
||||
+ depends on SND_BCM2708_SOC_I2S
|
||||
+ select SND_SOC_WM8804
|
||||
+ help
|
||||
+ Say Y or M if you want to add support for HifiBerry Digi S/PDIF output board.
|
||||
+
|
||||
config SND_BCM2708_SOC_RPI_DAC
|
||||
tristate "Support for RPi-DAC"
|
||||
depends on SND_BCM2708_SOC_I2S
|
||||
--- a/sound/soc/bcm/Makefile
|
||||
+++ b/sound/soc/bcm/Makefile
|
||||
@@ -10,7 +10,9 @@ obj-$(CONFIG_SND_BCM2708_SOC_I2S) += snd
|
||||
|
||||
# BCM2708 Machine Support
|
||||
snd-soc-hifiberry-dac-objs := hifiberry_dac.o
|
||||
+snd-soc-hifiberry-digi-objs := hifiberry_digi.o
|
||||
snd-soc-rpi-dac-objs := rpi-dac.o
|
||||
|
||||
obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) += snd-soc-hifiberry-dac.o
|
||||
+obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) += snd-soc-hifiberry-digi.o
|
||||
obj-$(CONFIG_SND_BCM2708_SOC_RPI_DAC) += snd-soc-rpi-dac.o
|
||||
--- /dev/null
|
||||
+++ b/sound/soc/bcm/hifiberry_digi.c
|
||||
@@ -0,0 +1,153 @@
|
||||
+/*
|
||||
+ * ASoC Driver for HifiBerry Digi
|
||||
+ *
|
||||
+ * Author: Daniel Matuschek <info@crazy-audio.com>
|
||||
+ * based on the HifiBerry DAC driver by Florian Meier <florian.meier@koalo.de>
|
||||
+ * Copyright 2013
|
||||
+ *
|
||||
+ * 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 <sound/core.h>
|
||||
+#include <sound/pcm.h>
|
||||
+#include <sound/pcm_params.h>
|
||||
+#include <sound/soc.h>
|
||||
+#include <sound/jack.h>
|
||||
+
|
||||
+#include "../codecs/wm8804.h"
|
||||
+
|
||||
+static int samplerate=44100;
|
||||
+
|
||||
+static int snd_rpi_hifiberry_digi_init(struct snd_soc_pcm_runtime *rtd)
|
||||
+{
|
||||
+ struct snd_soc_codec *codec = rtd->codec;
|
||||
+
|
||||
+ /* enable TX output */
|
||||
+ snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, 0x0);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int snd_rpi_hifiberry_digi_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_codec *codec = rtd->codec;
|
||||
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
+
|
||||
+ int sysclk = 27000000; /* This is fixed on this board */
|
||||
+
|
||||
+ long mclk_freq=0;
|
||||
+ int mclk_div=1;
|
||||
+
|
||||
+ int ret;
|
||||
+
|
||||
+ samplerate = params_rate(params);
|
||||
+
|
||||
+ switch (samplerate) {
|
||||
+ case 44100:
|
||||
+ case 48000:
|
||||
+ case 88200:
|
||||
+ case 96000:
|
||||
+ mclk_freq=samplerate*256;
|
||||
+ mclk_div=WM8804_MCLKDIV_256FS;
|
||||
+ break;
|
||||
+ case 176400:
|
||||
+ case 192000:
|
||||
+ mclk_freq=samplerate*128;
|
||||
+ mclk_div=WM8804_MCLKDIV_128FS;
|
||||
+ break;
|
||||
+ default:
|
||||
+ dev_err(substream->pcm->dev,
|
||||
+ "Failed to set WM8804 SYSCLK, unsupported samplerate\n");
|
||||
+ }
|
||||
+
|
||||
+ snd_soc_dai_set_clkdiv(codec_dai, WM8804_MCLK_DIV, mclk_div);
|
||||
+ snd_soc_dai_set_pll(codec_dai, 0, 0, sysclk, mclk_freq);
|
||||
+
|
||||
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8804_TX_CLKSRC_PLL,
|
||||
+ sysclk, SND_SOC_CLOCK_OUT);
|
||||
+ if (ret < 0) {
|
||||
+ dev_err(substream->pcm->dev,
|
||||
+ "Failed to set WM8804 SYSCLK: %d\n", ret);
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ /* Enable TX output */
|
||||
+ snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, 0x0);
|
||||
+
|
||||
+ /* Power on */
|
||||
+ snd_soc_update_bits(codec, WM8804_PWRDN, 0x9, 0);
|
||||
+
|
||||
+ return snd_soc_dai_set_bclk_ratio(cpu_dai,64);
|
||||
+}
|
||||
+
|
||||
+/* machine stream operations */
|
||||
+static struct snd_soc_ops snd_rpi_hifiberry_digi_ops = {
|
||||
+ .hw_params = snd_rpi_hifiberry_digi_hw_params,
|
||||
+};
|
||||
+
|
||||
+static struct snd_soc_dai_link snd_rpi_hifiberry_digi_dai[] = {
|
||||
+{
|
||||
+ .name = "HifiBerry Digi",
|
||||
+ .stream_name = "HifiBerry Digi HiFi",
|
||||
+ .cpu_dai_name = "bcm2708-i2s.0",
|
||||
+ .codec_dai_name = "wm8804-spdif",
|
||||
+ .platform_name = "bcm2708-i2s.0",
|
||||
+ .codec_name = "wm8804.1-003b",
|
||||
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
|
||||
+ SND_SOC_DAIFMT_CBM_CFM,
|
||||
+ .ops = &snd_rpi_hifiberry_digi_ops,
|
||||
+ .init = snd_rpi_hifiberry_digi_init,
|
||||
+},
|
||||
+};
|
||||
+
|
||||
+/* audio machine driver */
|
||||
+static struct snd_soc_card snd_rpi_hifiberry_digi = {
|
||||
+ .name = "snd_rpi_hifiberry_digi",
|
||||
+ .dai_link = snd_rpi_hifiberry_digi_dai,
|
||||
+ .num_links = ARRAY_SIZE(snd_rpi_hifiberry_digi_dai),
|
||||
+};
|
||||
+
|
||||
+static int snd_rpi_hifiberry_digi_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ snd_rpi_hifiberry_digi.dev = &pdev->dev;
|
||||
+ ret = snd_soc_register_card(&snd_rpi_hifiberry_digi);
|
||||
+ if (ret)
|
||||
+ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int snd_rpi_hifiberry_digi_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ return snd_soc_unregister_card(&snd_rpi_hifiberry_digi);
|
||||
+}
|
||||
+
|
||||
+static struct platform_driver snd_rpi_hifiberry_digi_driver = {
|
||||
+ .driver = {
|
||||
+ .name = "snd-hifiberry-digi",
|
||||
+ .owner = THIS_MODULE,
|
||||
+ },
|
||||
+ .probe = snd_rpi_hifiberry_digi_probe,
|
||||
+ .remove = snd_rpi_hifiberry_digi_remove,
|
||||
+};
|
||||
+
|
||||
+module_platform_driver(snd_rpi_hifiberry_digi_driver);
|
||||
+
|
||||
+MODULE_AUTHOR("Daniel Matuschek <info@crazy-audio.com>");
|
||||
+MODULE_DESCRIPTION("ASoC Driver for HifiBerry Digi");
|
||||
+MODULE_LICENSE("GPL v2");
|
|
@ -1,47 +0,0 @@
|
|||
From fc11caaeda3eec0b6338988f411b71151fee25a9 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Matuschek <info@crazy-audio.com>
|
||||
Date: Thu, 16 Jan 2014 07:26:08 +0100
|
||||
Subject: [PATCH 45/54] BCM2708: Added support for HiFiBerry Digi board Board
|
||||
initalization by I2C
|
||||
|
||||
Signed-off-by: Daniel Matuschek <daniel@matuschek.net>
|
||||
---
|
||||
arch/arm/mach-bcm2708/bcm2708.c | 20 ++++++++++++++++++++
|
||||
1 file changed, 20 insertions(+)
|
||||
|
||||
--- a/arch/arm/mach-bcm2708/bcm2708.c
|
||||
+++ b/arch/arm/mach-bcm2708/bcm2708.c
|
||||
@@ -651,6 +651,21 @@ static struct platform_device snd_pcm510
|
||||
};
|
||||
#endif
|
||||
|
||||
+#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI_MODULE)
|
||||
+static struct platform_device snd_hifiberry_digi_device = {
|
||||
+ .name = "snd-hifiberry-digi",
|
||||
+ .id = 0,
|
||||
+ .num_resources = 0,
|
||||
+};
|
||||
+
|
||||
+static struct i2c_board_info __initdata snd_wm8804_i2c_devices[] = {
|
||||
+ {
|
||||
+ I2C_BOARD_INFO("wm8804", 0x3b)
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+#endif
|
||||
+
|
||||
#if defined(CONFIG_SND_BCM2708_SOC_RPI_DAC) || defined(CONFIG_SND_BCM2708_SOC_RPI_DAC_MODULE)
|
||||
static struct platform_device snd_rpi_dac_device = {
|
||||
.name = "snd-rpi-dac",
|
||||
@@ -797,6 +812,11 @@ void __init bcm2708_init(void)
|
||||
bcm_register_device(&snd_pcm5102a_codec_device);
|
||||
#endif
|
||||
|
||||
+#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI_MODULE)
|
||||
+ bcm_register_device(&snd_hifiberry_digi_device);
|
||||
+ i2c_register_board_info(1, snd_wm8804_i2c_devices, ARRAY_SIZE(snd_wm8804_i2c_devices));
|
||||
+#endif
|
||||
+
|
||||
#if defined(CONFIG_SND_BCM2708_SOC_RPI_DAC) || defined(CONFIG_SND_BCM2708_SOC_RPI_DAC_MODULE)
|
||||
bcm_register_device(&snd_rpi_dac_device);
|
||||
bcm_register_device(&snd_pcm1794a_codec_device);
|
|
@ -1,25 +0,0 @@
|
|||
From f70ff551596e371431a05ab5678fb12f2dfc282a Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Matuschek <info@crazy-audio.com>
|
||||
Date: Thu, 16 Jan 2014 07:27:28 +0100
|
||||
Subject: [PATCH 46/54] BCM2708: Added HiFiBerry Digi configuration option It
|
||||
will be compiled as a module by default. This also includes the WM8804
|
||||
driver.
|
||||
|
||||
Signed-off-by: Daniel Matuschek <daniel@matuschek.net>
|
||||
---
|
||||
arch/arm/configs/bcmrpi_defconfig | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
--- a/arch/arm/configs/bcmrpi_defconfig
|
||||
+++ b/arch/arm/configs/bcmrpi_defconfig
|
||||
@@ -742,8 +742,10 @@ CONFIG_SND_USB_6FIRE=m
|
||||
CONFIG_SND_SOC=m
|
||||
CONFIG_SND_SOC_DMAENGINE_PCM=y
|
||||
CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM=y
|
||||
+CONFIG_SND_SOC_WM8804=m
|
||||
CONFIG_SND_BCM2708_SOC_I2S=m
|
||||
CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m
|
||||
+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI=m
|
||||
CONFIG_SND_BCM2708_SOC_RPI_DAC=m
|
||||
CONFIG_SND_SOC_I2C_AND_SPI=m
|
||||
CONFIG_SND_SOC_PCM5102A=m
|
|
@ -1,22 +0,0 @@
|
|||
From d3dd70a52c9da2cec7b1b9247834357e5e5166eb Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Matuschek <info@crazy-audio.com>
|
||||
Date: Thu, 16 Jan 2014 07:36:35 +0100
|
||||
Subject: [PATCH 47/54] ASoC: wm8804: Set idle_bias_off to false Idle bias has
|
||||
been change to remove warning on driver startup
|
||||
|
||||
Signed-off-by: Daniel Matuschek <daniel@matuschek.net>
|
||||
---
|
||||
sound/soc/codecs/wm8804.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/sound/soc/codecs/wm8804.c
|
||||
+++ b/sound/soc/codecs/wm8804.c
|
||||
@@ -685,7 +685,7 @@ static struct snd_soc_codec_driver soc_c
|
||||
.suspend = wm8804_suspend,
|
||||
.resume = wm8804_resume,
|
||||
.set_bias_level = wm8804_set_bias_level,
|
||||
- .idle_bias_off = true,
|
||||
+ .idle_bias_off = false,
|
||||
|
||||
.controls = wm8804_snd_controls,
|
||||
.num_controls = ARRAY_SIZE(wm8804_snd_controls),
|
|
@ -1,33 +0,0 @@
|
|||
From ea4cb320f037ddad635159ad0d34a4e7993a252b Mon Sep 17 00:00:00 2001
|
||||
From: popcornmix <popcornmix@gmail.com>
|
||||
Date: Wed, 12 Mar 2014 11:46:34 +0000
|
||||
Subject: [PATCH 48/54] ASoc: Don't report S24_LE support, it produces white
|
||||
noise with xbmc
|
||||
|
||||
---
|
||||
sound/soc/bcm/bcm2708-i2s.c | 2 +-
|
||||
sound/soc/codecs/pcm5102a.c | 2 +-
|
||||
2 files changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/sound/soc/bcm/bcm2708-i2s.c
|
||||
+++ b/sound/soc/bcm/bcm2708-i2s.c
|
||||
@@ -718,7 +718,7 @@ static struct snd_soc_dai_driver bcm2708
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_8000_192000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE
|
||||
- | SNDRV_PCM_FMTBIT_S24_LE
|
||||
+ // | SNDRV_PCM_FMTBIT_S24_LE : disable for now, it causes white noise with xbmc
|
||||
| SNDRV_PCM_FMTBIT_S32_LE
|
||||
},
|
||||
.capture = {
|
||||
--- a/sound/soc/codecs/pcm5102a.c
|
||||
+++ b/sound/soc/codecs/pcm5102a.c
|
||||
@@ -28,7 +28,7 @@ static struct snd_soc_dai_driver pcm5102
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_8000_192000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE |
|
||||
- SNDRV_PCM_FMTBIT_S24_LE |
|
||||
+ // SNDRV_PCM_FMTBIT_S24_LE | : disable for now, it causes white noise with xbmc
|
||||
SNDRV_PCM_FMTBIT_S32_LE
|
||||
},
|
||||
};
|
File diff suppressed because it is too large
Load diff
|
@ -1,73 +0,0 @@
|
|||
From a46efd685fc6c9676b68f02e8ef12c5821809364 Mon Sep 17 00:00:00 2001
|
||||
From: Howard Mitchell <hm@hmbedded.co.uk>
|
||||
Date: Fri, 28 Mar 2014 16:27:57 +0000
|
||||
Subject: [PATCH 50/54] Fix volsw_range functions so SOC_DOUBLE_R_RANGE_TLV
|
||||
works.
|
||||
|
||||
This is so that the correct rabge of values as specified
|
||||
with the SOC_DOUBLE_R_RANGE_TLV macro are sent to the
|
||||
hardware for both the normal and invert cases.
|
||||
---
|
||||
sound/soc/soc-core.c | 22 ++++++++++------------
|
||||
1 file changed, 10 insertions(+), 12 deletions(-)
|
||||
|
||||
--- a/sound/soc/soc-core.c
|
||||
+++ b/sound/soc/soc-core.c
|
||||
@@ -3038,8 +3038,8 @@ int snd_soc_info_volsw_range(struct snd_
|
||||
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1;
|
||||
- uinfo->value.integer.min = 0;
|
||||
- uinfo->value.integer.max = platform_max - min;
|
||||
+ uinfo->value.integer.min = min;
|
||||
+ uinfo->value.integer.max = platform_max;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -3070,9 +3070,10 @@ int snd_soc_put_volsw_range(struct snd_k
|
||||
unsigned int val, val_mask;
|
||||
int ret;
|
||||
|
||||
- val = ((ucontrol->value.integer.value[0] + min) & mask);
|
||||
if (invert)
|
||||
- val = max - val;
|
||||
+ val = ((max - ucontrol->value.integer.value[0] + min) & mask);
|
||||
+ else
|
||||
+ val = (ucontrol->value.integer.value[0] & mask);
|
||||
val_mask = mask << shift;
|
||||
val = val << shift;
|
||||
|
||||
@@ -3081,9 +3082,10 @@ int snd_soc_put_volsw_range(struct snd_k
|
||||
return ret;
|
||||
|
||||
if (snd_soc_volsw_is_stereo(mc)) {
|
||||
- val = ((ucontrol->value.integer.value[1] + min) & mask);
|
||||
if (invert)
|
||||
- val = max - val;
|
||||
+ val = ((max - ucontrol->value.integer.value[1] + min) & mask);
|
||||
+ else
|
||||
+ val = (ucontrol->value.integer.value[1] & mask);
|
||||
val_mask = mask << shift;
|
||||
val = val << shift;
|
||||
|
||||
@@ -3121,18 +3123,14 @@ int snd_soc_get_volsw_range(struct snd_k
|
||||
(snd_soc_read(codec, reg) >> shift) & mask;
|
||||
if (invert)
|
||||
ucontrol->value.integer.value[0] =
|
||||
- max - ucontrol->value.integer.value[0];
|
||||
- ucontrol->value.integer.value[0] =
|
||||
- ucontrol->value.integer.value[0] - min;
|
||||
+ max - ucontrol->value.integer.value[0] + min;
|
||||
|
||||
if (snd_soc_volsw_is_stereo(mc)) {
|
||||
ucontrol->value.integer.value[1] =
|
||||
(snd_soc_read(codec, rreg) >> shift) & mask;
|
||||
if (invert)
|
||||
ucontrol->value.integer.value[1] =
|
||||
- max - ucontrol->value.integer.value[1];
|
||||
- ucontrol->value.integer.value[1] =
|
||||
- ucontrol->value.integer.value[1] - min;
|
||||
+ max - ucontrol->value.integer.value[1] + min;
|
||||
}
|
||||
|
||||
return 0;
|
|
@ -1,24 +0,0 @@
|
|||
From 53f059eb1ce439b171516c0e6673ab88ca25e166 Mon Sep 17 00:00:00 2001
|
||||
From: Howard Mitchell <hm@hmbedded.co.uk>
|
||||
Date: Fri, 28 Mar 2014 16:40:31 +0000
|
||||
Subject: [PATCH 51/54] Use a range macro for Playback Volume.
|
||||
|
||||
This allows limiting the output gain to avoid clipping in the
|
||||
DAC ouput stages.
|
||||
---
|
||||
sound/soc/codecs/pcm512x.c | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/sound/soc/codecs/pcm512x.c
|
||||
+++ b/sound/soc/codecs/pcm512x.c
|
||||
@@ -240,8 +240,8 @@ static const struct soc_enum pcm512x_ved
|
||||
pcm512x_ramp_step_text);
|
||||
|
||||
static const struct snd_kcontrol_new pcm512x_controls[] = {
|
||||
-SOC_DOUBLE_R_TLV("Playback Digital Volume", PCM512x_DIGITAL_VOLUME_2,
|
||||
- PCM512x_DIGITAL_VOLUME_3, 0, 255, 1, digital_tlv),
|
||||
+SOC_DOUBLE_R_RANGE_TLV("Playback Digital Volume", PCM512x_DIGITAL_VOLUME_2,
|
||||
+ PCM512x_DIGITAL_VOLUME_3, 0, 48, 255, 1, digital_tlv),
|
||||
SOC_DOUBLE("Playback Digital Switch", PCM512x_MUTE, PCM512x_RQML_SHIFT,
|
||||
PCM512x_RQMR_SHIFT, 1, 1),
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
From 7d86ec6d4ad388d8287a68d31ff3e3028477fd86 Mon Sep 17 00:00:00 2001
|
||||
From: Gordon Garrity <gordon@iqaudio.com>
|
||||
Date: Sun, 30 Mar 2014 13:52:33 +0100
|
||||
Subject: [PATCH 52/54] fix soc-core's inverse range and let IQaudIO DAC use
|
||||
this fixed SOC_DOUBLE_R_RANGE_TLV support
|
||||
|
||||
---
|
||||
sound/soc/codecs/pcm512x.c | 3 ++-
|
||||
sound/soc/soc-core.c | 8 ++++----
|
||||
2 files changed, 6 insertions(+), 5 deletions(-)
|
||||
|
||||
--- a/sound/soc/codecs/pcm512x.c
|
||||
+++ b/sound/soc/codecs/pcm512x.c
|
||||
@@ -239,9 +239,10 @@ static const struct soc_enum pcm512x_ved
|
||||
SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_2, PCM512x_VEDS_SHIFT, 4,
|
||||
pcm512x_ramp_step_text);
|
||||
|
||||
+/* Don't let the DAC go into clipping by limiting the alsa volume control range */
|
||||
static const struct snd_kcontrol_new pcm512x_controls[] = {
|
||||
SOC_DOUBLE_R_RANGE_TLV("Playback Digital Volume", PCM512x_DIGITAL_VOLUME_2,
|
||||
- PCM512x_DIGITAL_VOLUME_3, 0, 48, 255, 1, digital_tlv),
|
||||
+ PCM512x_DIGITAL_VOLUME_3, 0, 40, 255, 1, digital_tlv),
|
||||
SOC_DOUBLE("Playback Digital Switch", PCM512x_MUTE, PCM512x_RQML_SHIFT,
|
||||
PCM512x_RQMR_SHIFT, 1, 1),
|
||||
|
||||
--- a/sound/soc/soc-core.c
|
||||
+++ b/sound/soc/soc-core.c
|
||||
@@ -3072,8 +3072,8 @@ int snd_soc_put_volsw_range(struct snd_k
|
||||
|
||||
if (invert)
|
||||
val = ((max - ucontrol->value.integer.value[0] + min) & mask);
|
||||
- else
|
||||
- val = (ucontrol->value.integer.value[0] & mask);
|
||||
+ else
|
||||
+ val = (ucontrol->value.integer.value[0] & mask);
|
||||
val_mask = mask << shift;
|
||||
val = val << shift;
|
||||
|
||||
@@ -3084,8 +3084,8 @@ int snd_soc_put_volsw_range(struct snd_k
|
||||
if (snd_soc_volsw_is_stereo(mc)) {
|
||||
if (invert)
|
||||
val = ((max - ucontrol->value.integer.value[1] + min) & mask);
|
||||
- else
|
||||
- val = (ucontrol->value.integer.value[1] & mask);
|
||||
+ else
|
||||
+ val = (ucontrol->value.integer.value[1] & mask);
|
||||
val_mask = mask << shift;
|
||||
val = val << shift;
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
From 3eae00b73609f4e5a19260bfa4ca9021f295c0af Mon Sep 17 00:00:00 2001
|
||||
From: Gordon Hollingworth <gordon@holliweb.co.uk>
|
||||
Date: Fri, 2 May 2014 16:13:59 +0100
|
||||
Subject: [PATCH 53/54] Move GPIO setup to hw_params.
|
||||
|
||||
This is used to stop the I2S driver from breaking
|
||||
the GPIO setup for other uses of the PCM interface
|
||||
---
|
||||
sound/soc/bcm/bcm2708-i2s.c | 55 +++++++++++++++++++++++----------------------
|
||||
1 file changed, 28 insertions(+), 27 deletions(-)
|
||||
|
||||
--- a/sound/soc/bcm/bcm2708-i2s.c
|
||||
+++ b/sound/soc/bcm/bcm2708-i2s.c
|
||||
@@ -305,6 +305,31 @@ static int bcm2708_i2s_set_dai_bclk_rati
|
||||
return 0;
|
||||
}
|
||||
|
||||
+
|
||||
+static void bcm2708_i2s_setup_gpio(void)
|
||||
+{
|
||||
+ /*
|
||||
+ * This is the common way to handle the GPIO pins for
|
||||
+ * the Raspberry Pi.
|
||||
+ * TODO Better way would be to handle
|
||||
+ * this in the device tree!
|
||||
+ */
|
||||
+#define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3))
|
||||
+#define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3))
|
||||
+
|
||||
+ unsigned int *gpio;
|
||||
+ int pin;
|
||||
+ gpio = ioremap(GPIO_BASE, SZ_16K);
|
||||
+
|
||||
+ /* SPI is on GPIO 7..11 */
|
||||
+ for (pin = 28; pin <= 31; pin++) {
|
||||
+ INP_GPIO(pin); /* set mode to GPIO input first */
|
||||
+ SET_GPIO_ALT(pin, 2); /* set mode to ALT 0 */
|
||||
+ }
|
||||
+#undef INP_GPIO
|
||||
+#undef SET_GPIO_ALT
|
||||
+}
|
||||
+
|
||||
static int bcm2708_i2s_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
@@ -334,6 +359,9 @@ static int bcm2708_i2s_hw_params(struct
|
||||
if (csreg & (BCM2708_I2S_TXON | BCM2708_I2S_RXON))
|
||||
return 0;
|
||||
|
||||
+
|
||||
+ bcm2708_i2s_setup_gpio();
|
||||
+
|
||||
/*
|
||||
* Adjust the data length according to the format.
|
||||
* We prefill the half frame length with an integer
|
||||
@@ -790,31 +818,6 @@ static const struct snd_soc_component_dr
|
||||
.name = "bcm2708-i2s-comp",
|
||||
};
|
||||
|
||||
-
|
||||
-static void bcm2708_i2s_setup_gpio(void)
|
||||
-{
|
||||
- /*
|
||||
- * This is the common way to handle the GPIO pins for
|
||||
- * the Raspberry Pi.
|
||||
- * TODO Better way would be to handle
|
||||
- * this in the device tree!
|
||||
- */
|
||||
-#define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3))
|
||||
-#define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3))
|
||||
-
|
||||
- unsigned int *gpio;
|
||||
- int pin;
|
||||
- gpio = ioremap(GPIO_BASE, SZ_16K);
|
||||
-
|
||||
- /* SPI is on GPIO 7..11 */
|
||||
- for (pin = 28; pin <= 31; pin++) {
|
||||
- INP_GPIO(pin); /* set mode to GPIO input first */
|
||||
- SET_GPIO_ALT(pin, 2); /* set mode to ALT 0 */
|
||||
- }
|
||||
-#undef INP_GPIO
|
||||
-#undef SET_GPIO_ALT
|
||||
-}
|
||||
-
|
||||
static const struct snd_pcm_hardware bcm2708_pcm_hardware = {
|
||||
.info = SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_JOINT_DUPLEX,
|
||||
@@ -865,8 +868,6 @@ static int bcm2708_i2s_probe(struct plat
|
||||
if (IS_ERR(dev))
|
||||
return PTR_ERR(dev);
|
||||
|
||||
- bcm2708_i2s_setup_gpio();
|
||||
-
|
||||
dev->i2s_regmap = regmap[0];
|
||||
dev->clk_regmap = regmap[1];
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
From 2bdcc63f2648ad4dfa88aaf035e025b2b193a1cc Mon Sep 17 00:00:00 2001
|
||||
From: popcornmix <popcornmix@gmail.com>
|
||||
Date: Fri, 9 May 2014 15:45:13 +0100
|
||||
Subject: [PATCH 54/54] fb: distinguish physical and bus addresses
|
||||
|
||||
---
|
||||
drivers/video/bcm2708_fb.c | 15 +++++++++------
|
||||
1 file changed, 9 insertions(+), 6 deletions(-)
|
||||
|
||||
--- a/drivers/video/bcm2708_fb.c
|
||||
+++ b/drivers/video/bcm2708_fb.c
|
||||
@@ -89,6 +89,7 @@ struct bcm2708_fb {
|
||||
struct dentry *debugfs_dir;
|
||||
wait_queue_head_t dma_waitq;
|
||||
struct bcm2708_fb_stats stats;
|
||||
+ unsigned long fb_bus_address;
|
||||
};
|
||||
|
||||
#define to_bcm2708(info) container_of(info, struct bcm2708_fb, fb)
|
||||
@@ -314,13 +315,15 @@ static int bcm2708_fb_set_par(struct fb_
|
||||
else
|
||||
fb->fb.fix.visual = FB_VISUAL_TRUECOLOR;
|
||||
|
||||
+ fb->fb_bus_address = fbinfo->base;
|
||||
+ fbinfo->base &= ~0xc0000000;
|
||||
fb->fb.fix.smem_start = fbinfo->base;
|
||||
fb->fb.fix.smem_len = fbinfo->pitch * fbinfo->yres_virtual;
|
||||
fb->fb.screen_size = fbinfo->screen_size;
|
||||
if (fb->fb.screen_base)
|
||||
iounmap(fb->fb.screen_base);
|
||||
fb->fb.screen_base =
|
||||
- (void *)ioremap_wc(fb->fb.fix.smem_start, fb->fb.screen_size);
|
||||
+ (void *)ioremap_wc(fbinfo->base, fb->fb.screen_size);
|
||||
if (!fb->fb.screen_base) {
|
||||
/* the console may currently be locked */
|
||||
console_trylock();
|
||||
@@ -331,7 +334,7 @@ static int bcm2708_fb_set_par(struct fb_
|
||||
}
|
||||
print_debug
|
||||
("BCM2708FB: start = %p,%p width=%d, height=%d, bpp=%d, pitch=%d size=%d success=%d\n",
|
||||
- (void *)fb->fb.screen_base, (void *)fb->fb.fix.smem_start,
|
||||
+ (void *)fb->fb.screen_base, (void *)fb->fb_bus_address,
|
||||
fbinfo->xres, fbinfo->yres, fbinfo->bpp,
|
||||
fbinfo->pitch, (int)fb->fb.screen_size, val);
|
||||
|
||||
@@ -457,11 +460,11 @@ static void bcm2708_fb_copyarea(struct f
|
||||
|
||||
for (y = 0; y < region->height; y += scanlines_per_cb) {
|
||||
dma_addr_t src =
|
||||
- fb->fb.fix.smem_start +
|
||||
+ fb->fb_bus_address +
|
||||
bytes_per_pixel * region->sx +
|
||||
(region->sy + y) * fb->fb.fix.line_length;
|
||||
dma_addr_t dst =
|
||||
- fb->fb.fix.smem_start +
|
||||
+ fb->fb_bus_address +
|
||||
bytes_per_pixel * region->dx +
|
||||
(region->dy + y) * fb->fb.fix.line_length;
|
||||
|
||||
@@ -499,10 +502,10 @@ static void bcm2708_fb_copyarea(struct f
|
||||
stride = -fb->fb.fix.line_length;
|
||||
}
|
||||
set_dma_cb(cb, burst_size,
|
||||
- fb->fb.fix.smem_start + dy * fb->fb.fix.line_length +
|
||||
+ fb->fb_bus_address + dy * fb->fb.fix.line_length +
|
||||
bytes_per_pixel * region->dx,
|
||||
stride,
|
||||
- fb->fb.fix.smem_start + sy * fb->fb.fix.line_length +
|
||||
+ fb->fb_bus_address + sy * fb->fb.fix.line_length +
|
||||
bytes_per_pixel * region->sx,
|
||||
stride,
|
||||
region->width * bytes_per_pixel,
|
Loading…
Reference in a new issue