├── programs ├── crtmpserver │ ├── .gitignore │ ├── jwplayer-6.10.zip │ ├── index.rtsp.html │ ├── index.flv.html │ └── flvplayback.lua ├── faac-1.28.tar.gz ├── lame-3.99.5.tar.gz ├── motion-mmal-opt.tar.gz ├── motion-rpi-cam.html ├── shutdown_button │ ├── Makefile │ ├── shutdown_button_init │ └── shutdown_button.c ├── bbpicam_stream ├── TEMPered-fix-broken-cmakelists.patch ├── temper2led_init ├── bbpicam ├── ffmpeg-reduce-max-interleave-delta.patch └── TEMPered-add-temper2led.patch ├── kernel ├── .gitignore ├── build_env ├── README ├── bcm2708-i2s-sync-to-gpclk0.patch ├── rpi-mbed-use-gpclk0-as-mclk.patch ├── add-rpi-mbed.patch └── add-leds-pca9635.patch ├── docs ├── day.jpg ├── night.jpg ├── pca9635.png ├── bbPiCam_mini.jpg ├── mic_circuit.png ├── reset_button.png ├── mbed-codec-mods.jpg ├── cap1988l-2-camera.jpg └── mbed_audio_codec_i2s.jpg ├── .gitmodules ├── tests ├── test_pca9635 └── plot_wave ├── LICENSE └── README.md /programs/crtmpserver/.gitignore: -------------------------------------------------------------------------------- 1 | jwplayer 2 | -------------------------------------------------------------------------------- /kernel/.gitignore: -------------------------------------------------------------------------------- 1 | modules/* 2 | modules.tar.gz 3 | -------------------------------------------------------------------------------- /docs/day.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasaw/bbPiCam/HEAD/docs/day.jpg -------------------------------------------------------------------------------- /docs/night.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasaw/bbPiCam/HEAD/docs/night.jpg -------------------------------------------------------------------------------- /docs/pca9635.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasaw/bbPiCam/HEAD/docs/pca9635.png -------------------------------------------------------------------------------- /docs/bbPiCam_mini.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasaw/bbPiCam/HEAD/docs/bbPiCam_mini.jpg -------------------------------------------------------------------------------- /docs/mic_circuit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasaw/bbPiCam/HEAD/docs/mic_circuit.png -------------------------------------------------------------------------------- /docs/reset_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasaw/bbPiCam/HEAD/docs/reset_button.png -------------------------------------------------------------------------------- /docs/mbed-codec-mods.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasaw/bbPiCam/HEAD/docs/mbed-codec-mods.jpg -------------------------------------------------------------------------------- /docs/cap1988l-2-camera.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasaw/bbPiCam/HEAD/docs/cap1988l-2-camera.jpg -------------------------------------------------------------------------------- /programs/faac-1.28.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasaw/bbPiCam/HEAD/programs/faac-1.28.tar.gz -------------------------------------------------------------------------------- /programs/lame-3.99.5.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasaw/bbPiCam/HEAD/programs/lame-3.99.5.tar.gz -------------------------------------------------------------------------------- /docs/mbed_audio_codec_i2s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasaw/bbPiCam/HEAD/docs/mbed_audio_codec_i2s.jpg -------------------------------------------------------------------------------- /programs/motion-mmal-opt.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasaw/bbPiCam/HEAD/programs/motion-mmal-opt.tar.gz -------------------------------------------------------------------------------- /programs/crtmpserver/jwplayer-6.10.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasaw/bbPiCam/HEAD/programs/crtmpserver/jwplayer-6.10.zip -------------------------------------------------------------------------------- /programs/motion-rpi-cam.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /programs/shutdown_button/Makefile: -------------------------------------------------------------------------------- 1 | ifneq ($(SRC),) 2 | VPATH=$(SRC) 3 | endif 4 | 5 | CFLAGS += -I. -I$(SRC) -Wall -std=c99 -D_BSD_SOURCE=1 -D_GNU_SOURCE=1 6 | 7 | all: shutdown_button 8 | 9 | shutdown_button: shutdown_button.o 10 | $(CC) -o $@ $^ 11 | 12 | clean: 13 | rm -rf *.o $(all) 14 | -------------------------------------------------------------------------------- /kernel/build_env: -------------------------------------------------------------------------------- 1 | export ARCH=arm 2 | export CROSS_COMPILE=/home/jooaun/work/bbPiCam/kernel/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin/arm-linux-gnueabihf- 3 | export INSTALL_MOD_PATH=/home/jooaun/work/bbPiCam/kernel/modules 4 | export KERNEL_SRC=/home/jooaun/work/bbPiCam/kernel/linux 5 | 6 | -------------------------------------------------------------------------------- /programs/bbpicam_stream: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | PATH=/sbin:/usr/sbin:/bin:/usr/bin 4 | 5 | sleep 10 6 | 7 | FIFO=/tmp/live.h264 8 | 9 | #raspivid -w 1280 -h 960 -fps 24 -g 24 -t 0 -b 600000 -o - | LD_LIBRARY_PATH=/usr/local/lib ffmpeg -fflags +nobuffer -re -i - -fflags +nobuffer -re -f alsa -ar 16000 -ac 2 -i hw:1,0 -map 0:0 -map 1:0 -c:v copy -strict -2 -c:a aac -b:a 16k -ac 1 -af "pan=1c|c0=c1" -f rtsp -metadata title=bbPiCam rtsp://0.0.0.0:554 10 | 11 | mkfifo $FIFO 12 | raspivid -w 1280 -h 960 -fps 24 -g 24 -t 0 -b 600000 -o $FIFO & 13 | export LD_LIBRARY_PATH=/usr/local/lib 14 | exec ffmpeg -fflags +nobuffer -re -i $FIFO -fflags +nobuffer -re -f alsa -ar 16000 -ac 2 -i hw:1,0 -map 0:0 -map 1:0 -c:v copy -strict -2 -c:a aac -b:a 16k -ac 1 -af "pan=1c|c0=c1" -f rtsp -metadata title=bbPiCam rtsp://0.0.0.0:554 15 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "kernel/tools"] 2 | path = kernel/tools 3 | url = git://github.com/raspberrypi/tools.git 4 | [submodule "kernel/linux"] 5 | path = kernel/linux 6 | url = https://github.com/raspberrypi/linux.git 7 | [submodule "programs/ffmpeg"] 8 | path = programs/ffmpeg 9 | url = git://source.ffmpeg.org/ffmpeg.git 10 | [submodule "programs/hidapi"] 11 | path = programs/hidapi 12 | url = https://github.com/signal11/hidapi.git 13 | [submodule "programs/motion-mmal"] 14 | path = programs/motion-mmal 15 | url = https://github.com/dozencrows/motion.git 16 | [submodule "programs/psips"] 17 | path = programs/psips 18 | url = https://github.com/AndyA/psips.git 19 | [submodule "programs/TEMPered"] 20 | path = programs/TEMPered 21 | url = https://github.com/edorfaus/TEMPered.git 22 | [submodule "programs/x264"] 23 | path = programs/x264 24 | url = git://git.videolan.org/x264 25 | -------------------------------------------------------------------------------- /tests/test_pca9635: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | LED_PATH="/sys/bus/i2c/devices/1-0007" 4 | 5 | echo 02020202020202020202020202020202 > ${LED_PATH}/led_state 6 | echo 000100200300400500600700800900a00b00c00d00e00f00 > ${LED_PATH}/led_brightness 7 | sleep 3 8 | 9 | echo 0ff100200300400500600700800900a00b00c00d00e00f00 > ${LED_PATH}/led_brightness 10 | sleep 3 11 | 12 | echo 0001ff200300400500600700800900a00b00c00d00e00f00 > ${LED_PATH}/led_brightness 13 | sleep 3 14 | 15 | echo 0001002ff300400500600700800900a00b00c00d00e00f00 > ${LED_PATH}/led_brightness 16 | sleep 3 17 | 18 | echo 0ff1002ff300400500600700800900a00b00c00d00e00f00 > ${LED_PATH}/led_brightness 19 | sleep 3 20 | 21 | echo 0ff1ff200300400500600700800900a00b00c00d00e00f00 > ${LED_PATH}/led_brightness 22 | sleep 3 23 | 24 | echo 0001ff2ff300400500600700800900a00b00c00d00e00f00 > ${LED_PATH}/led_brightness 25 | sleep 3 26 | 27 | echo 0ff1ff2ff3ff4ff5ff6ff7ff8ff9ffaffbffcffdffefffff > ${LED_PATH}/led_brightness 28 | sleep 3 29 | 30 | echo 000100200300400500600700800900a00b00c00d00e00f00 > ${LED_PATH}/led_brightness 31 | -------------------------------------------------------------------------------- /programs/TEMPered-fix-broken-cmakelists.patch: -------------------------------------------------------------------------------- 1 | commit dc366a2fbfeab5686241967968f36eaf1c458370 2 | Author: Joo Aun Saw 3 | Date: Fri Sep 19 17:18:35 2014 +1000 4 | 5 | fix broken cmakelists. 6 | 7 | diff --git a/CMakeLists.txt b/CMakeLists.txt 8 | index b3cd007..5c95f13 100644 9 | --- a/CMakeLists.txt 10 | +++ b/CMakeLists.txt 11 | @@ -17,7 +17,7 @@ if (NOT DEFINED CMAKE_INSTALL_LIBDIR) 12 | # directory with the name GNUInstallDirs (without the extension). 13 | endif() 14 | 15 | -option(BUILD_HIDAPI_SHARED "Build with shared version of HIDAPI" ON) 16 | +option(BUILD_HIDAPI_SHARED "Build with shared version of HIDAPI" OFF) 17 | 18 | option(BUILD_SHARED_LIB "Build shared version of tempered library" ON) 19 | option(BUILD_STATIC_LIB "Build static version of tempered library" OFF) 20 | @@ -54,7 +54,7 @@ else() 21 | ) 22 | set(HIDAPI_STATIC_OBJECT ${HIDAPI_OBJECT}) 23 | find_package(PkgConfig REQUIRED) 24 | - if (HIDAPI_OBJECT MATCHES \(-libusb|/libusb/(.libs/)?hid\)\\.o\$) 25 | + if (HIDAPI_OBJECT MATCHES \(-libusb|/libusb/\(.libs/\)?hid\)\\.o\$) 26 | pkg_check_modules(LIBUSB REQUIRED libusb-1.0) 27 | set(HIDAPI_LINK_LIBS ${LIBUSB_LIBRARIES} rt pthread) 28 | else() 29 | -------------------------------------------------------------------------------- /programs/temper2led_init: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ### BEGIN INIT INFO 3 | # Provides: temper2led 4 | # Required-Start: $local_fs $remote_fs 5 | # Required-Stop: $local_fs $remote_fs 6 | # Default-Start: 2 3 4 5 7 | # Default-Stop: 0 1 6 8 | # Short-Description: Temperature to LED 9 | # Description: Temperature to LED. 10 | ### END INIT INFO 11 | 12 | # Author: Joo Aun Saw 13 | 14 | PATH=/sbin:/usr/sbin:/bin:/usr/bin 15 | DESC="Temperature to LED" 16 | NAME=temper2led 17 | DAEMON=/opt/temper2led/temper2led 18 | PIDFILE=/var/run/$NAME.pid 19 | SCRIPTNAME=/etc/init.d/${NAME}_init 20 | 21 | [ -x $DAEMON ] || exit 0 22 | 23 | . /lib/init/vars.sh 24 | . /lib/lsb/init-functions 25 | 26 | 27 | 28 | case $1 in 29 | start) 30 | log_daemon_msg "Starting $DESC " "$NAME" 31 | start-stop-daemon --start --background --quiet --pidfile $PIDFILE --make-pidfile --exec $DAEMON 32 | status=$? 33 | log_end_msg $status 34 | ;; 35 | stop) 36 | log_daemon_msg "Stopping $DESC" "$NAME" 37 | start-stop-daemon --stop --quiet --pidfile $PIDFILE 38 | status=$? 39 | log_end_msg $status 40 | rm -f $PIDFILE 41 | ;; 42 | restart|force-reload) 43 | $0 stop && sleep 2 && $0 start 44 | ;; 45 | status) 46 | status_of_proc "$DAEMON" "$NAME" 47 | ;; 48 | *) 49 | echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload|status}" 50 | exit 2 51 | ;; 52 | esac 53 | -------------------------------------------------------------------------------- /programs/bbpicam: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ### BEGIN INIT INFO 3 | # Provides: bbpicam 4 | # Required-Start: $network $local_fs $remote_fs crtmpserver nginx 5 | # Required-Stop: $network $local_fs $remote_fs crtmpserver nginx 6 | # Default-Start: 3 4 5 7 | # Default-Stop: 0 1 6 8 | # Short-Description: Raspivid to ffmpeg 9 | # Description: Video capture via raspivid and ffmpeg. 10 | ### END INIT INFO 11 | 12 | # Author: Joo Aun Saw 13 | 14 | PATH=/sbin:/usr/sbin:/bin:/usr/bin 15 | DESC="Video Capture" 16 | NAME=bbpicam 17 | DAEMON=/opt/bbpicam/bbpicam_stream 18 | PIDFILE=/var/run/$NAME.pid 19 | SCRIPTNAME=/etc/init.d/$NAME 20 | 21 | [ -x $DAEMON ] || exit 0 22 | 23 | . /lib/init/vars.sh 24 | . /lib/lsb/init-functions 25 | 26 | 27 | 28 | case $1 in 29 | start) 30 | log_daemon_msg "Starting $DESC " "$NAME" 31 | start-stop-daemon --start --background --quiet --pidfile $PIDFILE --make-pidfile --exec $DAEMON 32 | status=$? 33 | log_end_msg $status 34 | ;; 35 | stop) 36 | log_daemon_msg "Stopping $DESC" "$NAME" 37 | start-stop-daemon --stop --quiet --pidfile $PIDFILE 38 | status=$? 39 | log_end_msg $status 40 | rm -f $PIDFILE 41 | ;; 42 | restart|force-reload) 43 | $0 stop && sleep 2 && $0 start 44 | ;; 45 | status) 46 | status_of_proc "$DAEMON" "$NAME" 47 | ;; 48 | *) 49 | echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload|status}" 50 | exit 2 51 | ;; 52 | esac 53 | 54 | -------------------------------------------------------------------------------- /programs/shutdown_button/shutdown_button_init: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ### BEGIN INIT INFO 3 | # Provides: shutdown_button 4 | # Required-Start: $local_fs $remote_fs 5 | # Required-Stop: $local_fs $remote_fs 6 | # Default-Start: 2 3 4 5 7 | # Default-Stop: 0 1 6 8 | # Short-Description: Shutdown Button 9 | # Description: Shuts down when shutdown button is pressed. 10 | ### END INIT INFO 11 | 12 | # Author: Joo Aun Saw 13 | 14 | PATH=/sbin:/usr/sbin:/bin:/usr/bin 15 | DESC="Shutdown Button" 16 | NAME=shutdown_button 17 | DAEMON=/opt/shutdown_button/shutdown_button 18 | PIDFILE=/var/run/$NAME.pid 19 | SCRIPTNAME=/etc/init.d/$NAME 20 | 21 | [ -x $DAEMON ] || exit 0 22 | 23 | . /lib/init/vars.sh 24 | . /lib/lsb/init-functions 25 | 26 | 27 | 28 | case $1 in 29 | start) 30 | log_daemon_msg "Starting $DESC " "$NAME" 31 | start-stop-daemon --start --background --quiet --pidfile $PIDFILE --make-pidfile --exec $DAEMON 32 | status=$? 33 | log_end_msg $status 34 | ;; 35 | stop) 36 | log_daemon_msg "Stopping $DESC" "$NAME" 37 | start-stop-daemon --stop --quiet --pidfile $PIDFILE 38 | status=$? 39 | log_end_msg $status 40 | rm -f $PIDFILE 41 | ;; 42 | restart|force-reload) 43 | $0 stop && sleep 2 && $0 start 44 | ;; 45 | status) 46 | status_of_proc "$DAEMON" "$NAME" 47 | ;; 48 | *) 49 | echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload|status}" 50 | exit 2 51 | ;; 52 | esac 53 | -------------------------------------------------------------------------------- /tests/plot_wave: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | """ Plots wav file """ 3 | 4 | import os 5 | current_dir = os.path.dirname(os.path.realpath(__file__)) 6 | import sys 7 | 8 | import wave 9 | import numpy as np 10 | import matplotlib.pyplot as plt 11 | 12 | #mpl.rcParams['examples.directory'] = current_dir 13 | 14 | 15 | 16 | 17 | 18 | def print_usage(prog_cmd): 19 | print "Usage: %s " % prog_cmd 20 | 21 | 22 | def run(): 23 | if len(sys.argv) != 2: 24 | print_usage(sys.argv[0]) 25 | sys.exit(-1) 26 | 27 | wavefile = sys.argv[1] 28 | 29 | if not os.path.isfile(wavefile): 30 | print "Error: Wave file \"%s\" not found." % wavefile 31 | sys.exit(-1) 32 | 33 | spf = wave.open(wavefile,'r') 34 | # Extract Raw Audio from Wav File 35 | raw = spf.readframes(-1) 36 | signal = np.fromstring(raw, 'Int16').reshape((-1,spf.getnchannels())) 37 | 38 | fig = plt.figure() 39 | 40 | # Write raw samples to file 41 | #outfile = 'raw.out' 42 | #with open(outfile, "w") as ofile: 43 | # for idx, values in enumerate(signal): 44 | # for v in values: 45 | # ofile.write("%11d\t" % v) 46 | # ofile.write("\n") 47 | 48 | for i in range(spf.getnchannels()): 49 | ax = fig.add_subplot(spf.getnchannels(), 1, i+1) 50 | ax.set_title('Channel %d' % (i+1)) 51 | ax.plot(signal[:, i]) 52 | 53 | plt.show() 54 | 55 | 56 | 57 | if __name__ == "__main__": 58 | run() 59 | -------------------------------------------------------------------------------- /programs/ffmpeg-reduce-max-interleave-delta.patch: -------------------------------------------------------------------------------- 1 | diff --git a/libavformat/options_table.h b/libavformat/options_table.h 2 | index eb4115c..f4c3813 100644 3 | --- a/libavformat/options_table.h 4 | +++ b/libavformat/options_table.h 5 | @@ -88,7 +88,7 @@ static const AVOption avformat_options[] = { 6 | {"flush_packets", "enable flushing of the I/O context after each packet", OFFSET(flush_packets), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, E}, 7 | {"metadata_header_padding", "set number of bytes to be written as padding in a metadata header", OFFSET(metadata_header_padding), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, E}, 8 | {"output_ts_offset", "set output timestamp offset", OFFSET(output_ts_offset), AV_OPT_TYPE_DURATION, {.i64 = 0}, -INT64_MAX, INT64_MAX, E}, 9 | -{"max_interleave_delta", "maximum buffering duration for interleaving", OFFSET(max_interleave_delta), AV_OPT_TYPE_INT64, { .i64 = 10000000 }, 0, INT64_MAX, E }, 10 | +{"max_interleave_delta", "maximum buffering duration for interleaving", OFFSET(max_interleave_delta), AV_OPT_TYPE_INT64, { .i64 = 500000 }, 0, INT64_MAX, E }, 11 | {"f_strict", "how strictly to follow the standards (deprecated; use strict, save via avconv)", OFFSET(strict_std_compliance), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX, D|E, "strict"}, 12 | {"strict", "how strictly to follow the standards", OFFSET(strict_std_compliance), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX, D|E, "strict"}, 13 | {"strict", "strictly conform to all the things in the spec no matter what the consequences", 0, AV_OPT_TYPE_CONST, {.i64 = FF_COMPLIANCE_STRICT }, INT_MIN, INT_MAX, D|E, "strict"}, 14 | -------------------------------------------------------------------------------- /programs/crtmpserver/index.rtsp.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | BB Pi Camera RTMP stream 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 | 16 | 17 | 18 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /programs/crtmpserver/index.flv.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Raspberry Pi Camera RTMP stream 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 | 16 | 17 | 18 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /programs/crtmpserver/flvplayback.lua: -------------------------------------------------------------------------------- 1 | application= 2 | { 3 | description="FLV Playback Sample", 4 | name="flvplayback", 5 | protocol="dynamiclinklibrary", 6 | mediaFolder="/var/lib/crtmpserver/mediaFolder", 7 | aliases= 8 | { 9 | "simpleLive", 10 | "vod", 11 | "live", 12 | "WeeklyQuest", 13 | "SOSample", 14 | "oflaDemo", 15 | }, 16 | acceptors = 17 | { 18 | { 19 | ip="0.0.0.0", 20 | port=6666, 21 | protocol="inboundLiveFlv", 22 | waitForMetadata=true, 23 | }, 24 | { 25 | ip="0.0.0.0", 26 | port=9999, 27 | protocol="inboundTcpTs" 28 | }, 29 | { 30 | ip="0.0.0.0", 31 | port=554, 32 | protocol="inboundRtsp" 33 | }, 34 | --[[{ 35 | ip="0.0.0.0", 36 | port=7654, 37 | protocol="inboundRawHttpStream", 38 | crossDomainFile="/tmp/crossdomain.xml" 39 | },]]-- 40 | }, 41 | externalStreams = 42 | { 43 | --[[ 44 | { 45 | uri="rtsp://fms20.mediadirect.ro/live2/realitatea/realitatea", 46 | localStreamName="rtsp_test", 47 | forceTcp=true 48 | }, 49 | { 50 | uri="rtmp://edge01.fms.dutchview.nl/botr/bunny", 51 | localStreamName="rtmp_test", 52 | swfUrl="http://www.example.com/example.swf"; 53 | pageUrl="http://www.example.com/"; 54 | emulateUserAgent="MAC 10,1,82,76", 55 | }]]-- 56 | }, 57 | validateHandshake=false, 58 | keyframeSeek=false, 59 | seekGranularity=0.1, --in seconds, between 0.1 and 600 60 | clientSideBuffer=30, --in seconds, between 5 and 30 61 | --generateMetaFiles=true, --this will generate seek/meta files on application startup 62 | --renameBadFiles=false, 63 | --enableCheckBandwidth=true, 64 | --[[authentication= 65 | { 66 | rtmp={ 67 | type="adobe", 68 | encoderAgents= 69 | { 70 | "FMLE/3.0 (compatible; FMSc/1.0)", 71 | "My user agent", 72 | }, 73 | usersFile="/etc/crtmpserver/conf.d/users.lua" 74 | }, 75 | rtsp={ 76 | usersFile="/etc/crtmpserver/conf.d/users.lua" 77 | } 78 | }, --]] 79 | } 80 | 81 | -------------------------------------------------------------------------------- /kernel/README: -------------------------------------------------------------------------------- 1 | How to install custom Raspbian kernel with mbed Audio CODEC support: 2 | ==================================================================== 3 | 4 | # Clone the Raspbian kernel source 5 | git clone https://github.com/raspberrypi/linux.git 6 | 7 | # Clone koalo's kernel source that contains mbed driver 8 | git clone git://github.com/koalo/linux.git linux_koalo 9 | cd linux_koalo 10 | git checkout remotes/origin/rpi-3.8.y-asocdev 11 | 12 | # Port mbed driver across to Raspbian kernel 13 | # Copy linux_koalo/sound/soc/bcm2708/rpi-mbed.c to linux/sound/soc/bcm directory. 14 | # Update linux/sound/soc/bcm/Kconfig and linux/sound/soc/bcm/Makefile 15 | # Add mbed I2C device info in linux/arch/arm/march-bcm2708/bcm2708.c 16 | 17 | # Load environment variables 18 | . build_env 19 | 20 | # Clean up 21 | cd linux 22 | make mrproper 23 | cd - 24 | 25 | # Get kernel config from Pi running Raspbian: 26 | zcat /proc/config.gz > /tmp/.config 27 | 28 | # Copy .config file to linux directory: 29 | scp pi@rpi-cam:/tmp/.config ./linux 30 | 31 | # Enable rpi_mbed under: 32 | # Device Drivers 33 | # > Sound card support 34 | # > Advanced Linux Sound Architecture 35 | # > ALSA for SoC audio support 36 | # > SoC Audio support for the Broadcom BCM2708 I2S module 37 | cd linux 38 | make oldconfig 39 | make menuconfig 40 | 41 | # Compile kernel 42 | make -j4 43 | cd - 44 | 45 | # Install kernel modules 46 | mkdir modules 47 | cd linux 48 | make modules_install 49 | cd - 50 | 51 | # Create kernel.img from zImage 52 | cd tools/mkimage 53 | ./imagetool-uncompressed.py ${KERNEL_SRC}/arch/arm/boot/zImage 54 | cd - 55 | 56 | # Copy kernel.img to the Pi 57 | scp tools/mkimage/kernel.img pi@rpi-cam:/tmp 58 | 59 | # On the Pi 60 | cd /boot 61 | sudo mv kernel.img kernel.img.org 62 | sudo mv /tmp/kernel.img . 63 | 64 | # Before transfering kernel modules over to the Pi, remove symlinks 65 | rm modules/lib/modules/3.12.26+/build modules/lib/modules/3.12.26+/source 66 | tar czf modules.tar.gz modules/ 67 | scp modules.tar.gz pi@rpi-cam:/tmp 68 | 69 | # On the Pi 70 | cd /tmp 71 | tar xzf modules.tar.gz 72 | cd /lib 73 | mv modules modules_org 74 | sudo mv /tmp/modules/lib/modules /lib 75 | sudo chown -R root:root /lib/modules 76 | sudo reboot 77 | 78 | # add below lines to /etc/modules 79 | snd_soc_bcm2708 80 | snd_soc_bcm2708_i2s 81 | bcm2708_dmaengine 82 | snd_soc_tlv320aic23 83 | snd_soc_rpi_mbed 84 | 85 | # debug 86 | sudo modprobe i2c-bcm2708 87 | sudo modprobe i2c-dev 88 | sudo apt-get install i2c-tools 89 | i2cdetect 1 90 | 91 | # record 92 | alsamixer -c 1 93 | arecord -D hw:1,0 -f DAT -r 8 /tmp/my_record.wav 94 | 95 | -------------------------------------------------------------------------------- /kernel/bcm2708-i2s-sync-to-gpclk0.patch: -------------------------------------------------------------------------------- 1 | diff --git a/arch/arm/mach-bcm2708/include/mach/platform.h b/arch/arm/mach-bcm2708/include/mach/platform.h 2 | index 2e7e1bb..2921485 100644 3 | --- a/arch/arm/mach-bcm2708/include/mach/platform.h 4 | +++ b/arch/arm/mach-bcm2708/include/mach/platform.h 5 | @@ -62,6 +62,7 @@ 6 | #define DMA_BASE (BCM2708_PERI_BASE + 0x7000) /* DMA controller */ 7 | #define ARM_BASE (BCM2708_PERI_BASE + 0xB000) /* BCM2708 ARM control block */ 8 | #define PM_BASE (BCM2708_PERI_BASE + 0x100000) /* Power Management, Reset controller and Watchdog registers */ 9 | +#define GPCLK_BASE (BCM2708_PERI_BASE + 0x101000) /* General Purpose Clock */ 10 | #define PCM_CLOCK_BASE (BCM2708_PERI_BASE + 0x101098) /* PCM Clock */ 11 | #define RNG_BASE (BCM2708_PERI_BASE + 0x104000) /* Hardware RNG */ 12 | #define GPIO_BASE (BCM2708_PERI_BASE + 0x200000) /* GPIO */ 13 | diff --git a/sound/soc/bcm/bcm2708-i2s.c b/sound/soc/bcm/bcm2708-i2s.c 14 | index 905f076..c08c5a8 100644 15 | --- a/sound/soc/bcm/bcm2708-i2s.c 16 | +++ b/sound/soc/bcm/bcm2708-i2s.c 17 | @@ -50,6 +50,13 @@ 18 | 19 | #include 20 | 21 | +#define BCM2708_I2S_SYNC_TO_GPCLK0 22 | + 23 | +#ifdef BCM2708_I2S_SYNC_TO_GPCLK0 24 | +#define BCM2708_GPCLK_CTL_REG (0x70/4) 25 | +#define BCM2708_GPCLK_DIV_REG (0x74/4) 26 | +#endif 27 | + 28 | /* Clock registers */ 29 | #define BCM2708_CLK_PCMCTL_REG 0x00 30 | #define BCM2708_CLK_PCMDIV_REG 0x04 31 | @@ -66,6 +73,7 @@ 32 | 33 | #define BCM2708_CLK_SHIFT (12) 34 | #define BCM2708_CLK_DIVI(v) ((v) << BCM2708_CLK_SHIFT) 35 | +#define BCM2708_CLK_DIVI_MASK (0xFFF000) 36 | #define BCM2708_CLK_DIVF(v) (v) 37 | #define BCM2708_CLK_DIVF_MASK (0xFFF) 38 | 39 | @@ -454,6 +462,43 @@ static int bcm2708_i2s_hw_params(struct snd_pcm_substream *substream, 40 | clk_src = BCM2708_CLK_SRC_OSC; 41 | mash = BCM2708_CLK_MASH_0; 42 | 43 | +#ifdef BCM2708_I2S_SYNC_TO_GPCLK0 44 | + { 45 | + unsigned int *gpclk_map; 46 | + uint64_t gpclk_dividend; 47 | + uint64_t i2s_dividend; 48 | + 49 | + (void)frame_master; 50 | + (void)bit_master; 51 | + 52 | + if (!dev->bclk_ratio) { 53 | + /* 54 | + * Overwrite bclk_ratio, because the 55 | + * above trick is not needed or can 56 | + * not be used. 57 | + */ 58 | + bclk_ratio = 2 * data_length; 59 | + } 60 | + 61 | + target_frequency = sampling_rate * bclk_ratio; 62 | + 63 | + clk_src = BCM2708_CLK_SRC_PLLD; 64 | + mash = BCM2708_CLK_MASH_1; 65 | + 66 | + i2s_dividend = bcm2708_clk_freq[clk_src]; 67 | + i2s_dividend <<= BCM2708_CLK_SHIFT; 68 | + do_div(i2s_dividend, target_frequency); 69 | + 70 | + /* Work out an interger ratio so both clocks are in sync */ 71 | + gpclk_map = ioremap(GPCLK_BASE, SZ_4K); 72 | + gpclk_dividend = *(gpclk_map + BCM2708_GPCLK_DIV_REG); 73 | + i2s_dividend += (gpclk_dividend >> 1); 74 | + do_div(i2s_dividend, gpclk_dividend); 75 | + i2s_dividend *= gpclk_dividend; 76 | + divi = i2s_dividend >> BCM2708_CLK_SHIFT; 77 | + divf = i2s_dividend & BCM2708_CLK_DIVF_MASK; 78 | + } 79 | +#else 80 | if (bcm2708_clk_freq[clk_src] % target_frequency == 0 81 | && bit_master && frame_master) { 82 | divi = bcm2708_clk_freq[clk_src] / target_frequency; 83 | @@ -481,6 +526,7 @@ static int bcm2708_i2s_hw_params(struct snd_pcm_substream *substream, 84 | divi = dividend >> BCM2708_CLK_SHIFT; 85 | divf = dividend & BCM2708_CLK_DIVF_MASK; 86 | } 87 | +#endif 88 | 89 | /* Set clock divider */ 90 | regmap_write(dev->clk_regmap, BCM2708_CLK_PCMDIV_REG, BCM2708_CLK_PASSWD 91 | -------------------------------------------------------------------------------- /programs/shutdown_button/shutdown_button.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Filename: shutdown_button.c 3 | * Description: Watches the shutdown button and shutdown when pressed. 4 | * 5 | * Author: Copyright (C) Joo Aun Saw 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License version 2 as 9 | * published by the Free Software Foundation. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #define IN 0 29 | #define OUT 1 30 | 31 | #define LOW 0 32 | #define HIGH 1 33 | 34 | #define PIN 17 /* P1-11 */ 35 | 36 | static int terminated = 0; 37 | 38 | static int GPIOExport(int pin) 39 | { 40 | #define BUFFER_MAX 3 41 | char buffer[BUFFER_MAX]; 42 | ssize_t bytes_written; 43 | int fd; 44 | 45 | fd = open("/sys/class/gpio/export", O_WRONLY); 46 | if (-1 == fd) { 47 | fprintf(stderr, "Failed to open export for writing!\n"); 48 | return(-1); 49 | } 50 | 51 | bytes_written = snprintf(buffer, BUFFER_MAX, "%d", pin); 52 | write(fd, buffer, bytes_written); 53 | close(fd); 54 | return(0); 55 | } 56 | 57 | static int GPIOUnexport(int pin) 58 | { 59 | char buffer[BUFFER_MAX]; 60 | ssize_t bytes_written; 61 | int fd; 62 | 63 | fd = open("/sys/class/gpio/unexport", O_WRONLY); 64 | if (-1 == fd) { 65 | fprintf(stderr, "Failed to open unexport for writing!\n"); 66 | return(-1); 67 | } 68 | 69 | bytes_written = snprintf(buffer, BUFFER_MAX, "%d", pin); 70 | write(fd, buffer, bytes_written); 71 | close(fd); 72 | return(0); 73 | } 74 | 75 | static int GPIODirection(int pin, int dir) 76 | { 77 | static const char s_directions_str[] = "in\0out"; 78 | 79 | #define DIRECTION_MAX 35 80 | char path[DIRECTION_MAX]; 81 | int fd; 82 | 83 | snprintf(path, DIRECTION_MAX, "/sys/class/gpio/gpio%d/direction", pin); 84 | fd = open(path, O_WRONLY); 85 | if (-1 == fd) { 86 | fprintf(stderr, "Failed to open gpio direction for writing!\n"); 87 | return(-1); 88 | } 89 | 90 | if (-1 == write(fd, &s_directions_str[IN == dir ? 0 : 3], IN == dir ? 2 : 3)) { 91 | fprintf(stderr, "Failed to set direction!\n"); 92 | return(-1); 93 | } 94 | 95 | close(fd); 96 | return(0); 97 | } 98 | 99 | static int GPIORead(int pin) 100 | { 101 | #define VALUE_MAX 30 102 | char path[VALUE_MAX]; 103 | char value_str[3]; 104 | int fd; 105 | 106 | snprintf(path, VALUE_MAX, "/sys/class/gpio/gpio%d/value", pin); 107 | fd = open(path, O_RDONLY); 108 | if (-1 == fd) { 109 | fprintf(stderr, "Failed to open gpio value for reading!\n"); 110 | return(-1); 111 | } 112 | 113 | if (-1 == read(fd, value_str, 3)) { 114 | fprintf(stderr, "Failed to read value!\n"); 115 | return(-1); 116 | } 117 | 118 | close(fd); 119 | 120 | return(atoi(value_str)); 121 | } 122 | 123 | int main(int argc, char *argv[]) 124 | { 125 | /* 126 | * Enable GPIO pins 127 | */ 128 | if (-1 == GPIOExport(PIN)) 129 | return(1); 130 | 131 | /* 132 | * Set GPIO directions 133 | */ 134 | if (-1 == GPIODirection(PIN, IN)) 135 | return(2); 136 | 137 | while (!terminated) 138 | { 139 | usleep(500 * 1000); 140 | if (GPIORead(PIN) == LOW) 141 | { 142 | terminated = 1; 143 | if (system("shutdown -h now") == -1) 144 | fprintf(stderr, "Failed to initiate shutdown!\n"); 145 | } 146 | } 147 | 148 | /* 149 | * Disable GPIO pins 150 | */ 151 | if (-1 == GPIOUnexport(PIN)) 152 | return(3); 153 | 154 | return(0); 155 | } 156 | -------------------------------------------------------------------------------- /kernel/rpi-mbed-use-gpclk0-as-mclk.patch: -------------------------------------------------------------------------------- 1 | --- ../linux_koalo/sound/soc/bcm2708/rpi-mbed.c 2014-08-11 16:36:48.249568953 +1000 2 | +++ sound/soc/bcm/rpi-mbed.c 2014-08-25 14:05:14.427528510 +1000 3 | @@ -12,6 +12,7 @@ 4 | 5 | #include 6 | #include 7 | +#include 8 | 9 | #include 10 | #include 11 | @@ -20,8 +21,160 @@ 12 | 13 | #include "../codecs/tlv320aic23.h" 14 | 15 | +#define MBED_CODEC_MCLK 12288000 16 | + 17 | +/* GP Clock pin */ 18 | +#define BCM2708_GPCLK0_GPIO_PIN4 4 19 | +#define BCM2708_GPCLK0_GPIO_PIN4_ALT 0 20 | + 21 | +/* GP Clock registers */ 22 | +#define BCM2708_GPCLK_CTL_REG (0x70/4) 23 | +#define BCM2708_GPCLK_DIV_REG (0x74/4) 24 | + 25 | +/* Clock register settings */ 26 | +#define BCM2708_CLK_PASSWD (0x5a000000) 27 | +#define BCM2708_CLK_PASSWD_MASK (0xff000000) 28 | +#define BCM2708_CLK_MASH(v) ((v) << 9) 29 | +#define BCM2708_CLK_FLIP BIT(8) 30 | +#define BCM2708_CLK_BUSY BIT(7) 31 | +#define BCM2708_CLK_KILL BIT(5) 32 | +#define BCM2708_CLK_ENAB BIT(4) 33 | +#define BCM2708_CLK_SRC(v) (v) 34 | + 35 | +#define BCM2708_CLK_SHIFT (12) 36 | +#define BCM2708_CLK_DIVI(v) ((v) << BCM2708_CLK_SHIFT) 37 | +#define BCM2708_CLK_DIVF(v) (v) 38 | +#define BCM2708_CLK_DIVF_MASK (0xFFF) 39 | + 40 | +enum { 41 | + BCM2708_CLK_MASH_0 = 0, 42 | + BCM2708_CLK_MASH_1, 43 | + BCM2708_CLK_MASH_2, 44 | + BCM2708_CLK_MASH_3, 45 | +}; 46 | + 47 | +enum { 48 | + BCM2708_CLK_SRC_GND = 0, 49 | + BCM2708_CLK_SRC_OSC, 50 | + BCM2708_CLK_SRC_DBG0, 51 | + BCM2708_CLK_SRC_DBG1, 52 | + BCM2708_CLK_SRC_PLLA, 53 | + BCM2708_CLK_SRC_PLLC, 54 | + BCM2708_CLK_SRC_PLLD, 55 | + BCM2708_CLK_SRC_HDMI, 56 | +}; 57 | + 58 | +/* Most clocks are not useable (freq = 0) */ 59 | +static const unsigned int bcm2708_clk_freq[BCM2708_CLK_SRC_HDMI+1] = { 60 | + [BCM2708_CLK_SRC_GND] = 0, 61 | + [BCM2708_CLK_SRC_OSC] = 19200000, 62 | + [BCM2708_CLK_SRC_DBG0] = 0, 63 | + [BCM2708_CLK_SRC_DBG1] = 0, 64 | + [BCM2708_CLK_SRC_PLLA] = 0, 65 | + [BCM2708_CLK_SRC_PLLC] = 0, 66 | + [BCM2708_CLK_SRC_PLLD] = 500000000, 67 | + [BCM2708_CLK_SRC_HDMI] = 0, 68 | +}; 69 | + 70 | +static void snd_rpi_mbed_setup_gpio(void) 71 | +{ 72 | + /* 73 | + * This is the common way to handle the GPIO pins for 74 | + * the Raspberry Pi. 75 | + * TODO Better way would be to handle 76 | + * this in the device tree! 77 | + */ 78 | +#define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3)) 79 | +#define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3)) 80 | + 81 | + unsigned int *gpio; 82 | + int pin,alt; 83 | + 84 | + gpio = ioremap(GPIO_BASE, SZ_16K); 85 | + 86 | + pin=BCM2708_GPCLK0_GPIO_PIN4; 87 | + alt=BCM2708_GPCLK0_GPIO_PIN4_ALT; 88 | + 89 | + /* configure GPCLK0 pin to correct ALT mode */ 90 | + INP_GPIO(pin); /* set mode to GPIO input first */ 91 | + SET_GPIO_ALT(pin, alt); /* set mode to ALT */ 92 | + 93 | +#undef INP_GPIO 94 | +#undef SET_GPIO_ALT 95 | +} 96 | + 97 | +#define GP_CLK0_CTL *(clk_map + BCM2708_GPCLK_CTL_REG) 98 | +#define GP_CLK0_DIV *(clk_map + BCM2708_GPCLK_DIV_REG) 99 | + 100 | +static void snd_rpi_mbed_start_gpclk(unsigned int *clk_map) 101 | +{ 102 | + unsigned int tmp; 103 | + 104 | + tmp = GP_CLK0_CTL; 105 | + tmp &= ~(BCM2708_CLK_PASSWD_MASK | BCM2708_CLK_ENAB); 106 | + tmp |= (BCM2708_CLK_PASSWD | BCM2708_CLK_ENAB); 107 | + GP_CLK0_CTL = tmp; 108 | +} 109 | + 110 | +static void snd_rpi_mbed_stop_gpclk(unsigned int *clk_map) 111 | +{ 112 | + int timeout = 1000; 113 | + unsigned int tmp; 114 | + 115 | + /* Stop clock */ 116 | + tmp = GP_CLK0_CTL; 117 | + tmp &= ~(BCM2708_CLK_PASSWD_MASK | BCM2708_CLK_ENAB); 118 | + tmp |= BCM2708_CLK_PASSWD; 119 | + GP_CLK0_CTL = tmp; 120 | + 121 | + /* Wait for the BUSY flag going down */ 122 | + while (--timeout) { 123 | + if (!(GP_CLK0_DIV & BCM2708_CLK_BUSY)) 124 | + break; 125 | + } 126 | + 127 | + if (!timeout) { 128 | + /* KILL the clock */ 129 | + tmp = GP_CLK0_CTL; 130 | + tmp &= ~(BCM2708_CLK_KILL | BCM2708_CLK_PASSWD_MASK); 131 | + tmp |= (BCM2708_CLK_KILL | BCM2708_CLK_PASSWD); 132 | + GP_CLK0_CTL = tmp; 133 | + } 134 | +} 135 | + 136 | +static void snd_rpi_mbed_setup_gpclk(void) 137 | +{ 138 | + unsigned int mash = BCM2708_CLK_MASH_1; 139 | + unsigned int divi, divf, target_frequency; 140 | + int clk_src = BCM2708_CLK_SRC_PLLD; 141 | + unsigned int *clk_map; 142 | + uint64_t dividend; 143 | + 144 | + clk_map = ioremap(GPCLK_BASE, SZ_4K); 145 | + 146 | + snd_rpi_mbed_stop_gpclk(clk_map); 147 | + 148 | + target_frequency = MBED_CODEC_MCLK; 149 | + 150 | + dividend = bcm2708_clk_freq[clk_src]; 151 | + dividend <<= BCM2708_CLK_SHIFT; 152 | + do_div(dividend, target_frequency); 153 | + divi = dividend >> BCM2708_CLK_SHIFT; 154 | + divf = dividend & BCM2708_CLK_DIVF_MASK; 155 | + 156 | + /* Set clock divider */ 157 | + GP_CLK0_DIV = BCM2708_CLK_PASSWD | BCM2708_CLK_DIVI(divi) | BCM2708_CLK_DIVF(divf); 158 | + 159 | + /* Setup clock, but don't start it yet */ 160 | + GP_CLK0_CTL = BCM2708_CLK_PASSWD | BCM2708_CLK_MASH(mash) | BCM2708_CLK_SRC(clk_src); 161 | + 162 | + snd_rpi_mbed_start_gpclk(clk_map); 163 | +} 164 | + 165 | static int snd_rpi_mbed_init(struct snd_soc_pcm_runtime *rtd) 166 | { 167 | + snd_rpi_mbed_setup_gpclk(); 168 | + snd_rpi_mbed_setup_gpio(); 169 | return 0; 170 | } 171 | 172 | @@ -32,11 +185,14 @@ static int snd_rpi_mbed_hw_params(struct 173 | struct snd_soc_dai *codec_dai = rtd->codec_dai; 174 | int sysclk; 175 | 176 | - sysclk = 12000000; /* this is fixed on this board */ 177 | + sysclk = MBED_CODEC_MCLK; /* this is clocked by GPCLK0 */ 178 | 179 | /* set tlv320aic23 sysclk */ 180 | snd_soc_dai_set_sysclk(codec_dai, 0, sysclk, 0); 181 | 182 | + /* configure tlv320aic23 i2s mode */ 183 | + snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_MASTER_MASK | SND_SOC_DAIFMT_FORMAT_MASK); 184 | + 185 | return 0; 186 | } 187 | 188 | @@ -51,7 +207,7 @@ static struct snd_soc_dai_link snd_rpi_m 189 | .stream_name = "TLV320AIC23 HiFi", 190 | .cpu_dai_name = "bcm2708-i2s.0", 191 | .codec_dai_name = "tlv320aic23-hifi", 192 | - .platform_name = "bcm2708-pcm-audio.0", 193 | + .platform_name = "bcm2708-i2s.0", 194 | .codec_name = "tlv320aic23-codec.1-001b", 195 | .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 196 | SND_SOC_DAIFMT_CBS_CFS, 197 | -------------------------------------------------------------------------------- /programs/TEMPered-add-temper2led.patch: -------------------------------------------------------------------------------- 1 | commit 2758de4d99a8bdba3b98b43b676fcf323de84765 2 | Author: Joo Aun Saw 3 | Date: Tue Sep 30 12:55:48 2014 +1000 4 | 5 | Add temper2led. 6 | 7 | diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt 8 | index 069f815..972534e 100644 9 | --- a/examples/CMakeLists.txt 10 | +++ b/examples/CMakeLists.txt 11 | @@ -16,3 +16,6 @@ target_link_libraries(read-all ${TEMPERED_LIB} ${HIDAPI_LINK_LIBS}) 12 | 13 | add_executable(read-repeat read-repeat.c ${HIDAPI_STATIC_OBJECT}) 14 | target_link_libraries(read-repeat ${TEMPERED_LIB} ${HIDAPI_LINK_LIBS}) 15 | + 16 | +add_executable(temper2led temper2led.c ${HIDAPI_STATIC_OBJECT}) 17 | +target_link_libraries(temper2led ${TEMPERED_LIB} ${HIDAPI_LINK_LIBS} -lm) 18 | diff --git a/examples/temper2led.c b/examples/temper2led.c 19 | new file mode 100644 20 | index 0000000..aa02308 21 | --- /dev/null 22 | +++ b/examples/temper2led.c 23 | @@ -0,0 +1,491 @@ 24 | +#include 25 | +#include 26 | +#include 27 | +#include 28 | +#include 29 | +#include 30 | +#include 31 | +#include 32 | +#include 33 | +#include 34 | +#include 35 | +#include 36 | +#include 37 | + 38 | +/** 39 | +This example shows how to open a single device, and read its sensors repeatedly. 40 | +*/ 41 | + 42 | +struct interface_write_text 43 | +{ 44 | + const char *name; 45 | + const char *data; 46 | +}; 47 | + 48 | +static int terminated = 0; 49 | +static const char *progname = ""; 50 | +static const char *temper_path = "/dev/hidraw1"; 51 | +static const char *ledctrl_path = "/sys/bus/i2c/devices/1-0007"; 52 | +static const char *temperature_output_path = "/run/temper2led/RDing_TEMPer2_out"; 53 | +static int poll_rate = 1; 54 | + 55 | +#define min(a,b) ( (a < b) ? a : b ) 56 | +#define max(a,b) ( (a > b) ? a : b ) 57 | + 58 | + 59 | +static void handle_terminate_signal(int sig) 60 | +{ 61 | + if ((sig == SIGTERM) || (sig == SIGINT)) 62 | + terminated = 1; 63 | +} 64 | + 65 | + 66 | +static int max_interface_name_length(struct interface_write_text *interface_data, int num_interfaces) 67 | +{ 68 | + int max = 0; 69 | + int len; 70 | + int i; 71 | + for (i = 0; i < num_interfaces; i++) 72 | + { 73 | + len = strlen(interface_data[i].name); 74 | + if (len > max) 75 | + max = len; 76 | + } 77 | + return max; 78 | +} 79 | + 80 | +static int open_and_write(const char *device_path, const char *write_string) 81 | +{ 82 | + int result = -1; 83 | + struct stat st; 84 | + FILE *fileptr = NULL; 85 | + 86 | + if (stat(device_path, &st) != 0) 87 | + { 88 | + fprintf(stderr, "Error: %s not found\n", device_path); 89 | + } 90 | + else if ((fileptr = fopen(device_path, "w")) == NULL) 91 | + { 92 | + fprintf(stderr, "Error: unable to open %s\n", device_path); 93 | + } 94 | + else if (fwrite(write_string, 1, strlen(write_string), fileptr) != strlen(write_string)) 95 | + { 96 | + fprintf(stderr, "Error: failed to write to %s\n", device_path); 97 | + } 98 | + else 99 | + { 100 | + result = 0; 101 | + } 102 | + if (fileptr) 103 | + { 104 | + fclose(fileptr); 105 | + fileptr = NULL; 106 | + } 107 | + return result; 108 | +} 109 | + 110 | +static int write_interface_data(const char *interface_dir, struct interface_write_text *interface_data, int num_interfaces) 111 | +{ 112 | + int error = 0; 113 | + int i; 114 | + int max_name_length = max_interface_name_length(interface_data, num_interfaces); 115 | + int interface_dir_length = strlen(interface_dir); 116 | + char *device_path = NULL; 117 | + int device_path_length = interface_dir_length + max_name_length + 1; 118 | + 119 | + device_path = malloc(device_path_length); 120 | + if (device_path == NULL) 121 | + { 122 | + fprintf(stderr, "Error: failed to allocate memory\n"); 123 | + return -1;; 124 | + } 125 | + memcpy(device_path, interface_dir, interface_dir_length); 126 | + device_path[interface_dir_length] = 0; 127 | + 128 | + for (i = 0; i < num_interfaces; i++) 129 | + { 130 | + strcpy(&device_path[interface_dir_length], interface_data[i].name); 131 | + error |= open_and_write(device_path, interface_data[i].data); 132 | + } 133 | + 134 | + free(device_path); 135 | + device_path = NULL; 136 | + 137 | + return error; 138 | +} 139 | + 140 | +static int configure_led_driver(const char *interface_dir) 141 | +{ 142 | + struct interface_write_text interface_data[] = 143 | + {{.name = "/sleep", .data = "0"}, 144 | + {.name = "/group_blink_enable", .data = "0"}, 145 | + {.name = "/group_blink_freq", .data = "17"}, 146 | + {.name = "/group_brightness_blink", .data = "ff"}, 147 | + {.name = "/led_brightness", .data = "000100200300400500600700800900a00b00c00d00e00f00"}, 148 | + {.name = "/led_state", .data = "02122232425262728292a2b2c2d2e2f2"},}; 149 | + return write_interface_data(interface_dir, interface_data, sizeof(interface_data)/sizeof(struct interface_write_text)); 150 | +} 151 | + 152 | +static int set_led_driver_pattern(const char *interface_dir, const char *pattern) 153 | +{ 154 | + struct interface_write_text interface_data[] = 155 | + {{.name = "/led_brightness", .data = pattern},}; 156 | + return write_interface_data(interface_dir, interface_data, sizeof(interface_data)/sizeof(struct interface_write_text)); 157 | +} 158 | + 159 | +static int validate_device(const char *device_dir, const char *device_name) 160 | +{ 161 | + struct stat st; 162 | + FILE *fileptr; 163 | + unsigned int i; 164 | + unsigned char found = 0; 165 | + char *device_path = NULL; 166 | + int device_path_length = strlen(device_dir) + strlen("/name") + 1; 167 | + char tmp_buffer[64]; 168 | + 169 | + device_path = malloc(device_path_length); 170 | + if (device_path == NULL) 171 | + { 172 | + fprintf(stderr, "Error: failed to allocate memory\n"); 173 | + return -1; 174 | + } 175 | + 176 | + strcpy(device_path, device_dir); 177 | + strcat(device_path, "/name"); 178 | + 179 | + if (stat(device_path, &st) != 0) 180 | + { 181 | + fprintf(stderr, "Error: %s not found\n", device_path); 182 | + free(device_path); 183 | + device_path = NULL; 184 | + return -1; 185 | + } 186 | + 187 | + if ((fileptr = fopen(device_path, "r")) == NULL) 188 | + { 189 | + fprintf(stderr, "Error: unable to open %s\n", device_path); 190 | + free(device_path); 191 | + device_path = NULL; 192 | + return -1; 193 | + } 194 | + 195 | + while (fgets(tmp_buffer, sizeof(tmp_buffer), fileptr) != NULL) 196 | + { 197 | + for (i = 0; i < strlen(tmp_buffer); i++) 198 | + { 199 | + if (!isalnum(tmp_buffer[i])) 200 | + { 201 | + tmp_buffer[i] = 0; 202 | + break; 203 | + } 204 | + } 205 | + //printf("\t%s\n", tmp_buffer); 206 | + if (strcmp(tmp_buffer, device_name) == 0) 207 | + { 208 | + found = 1; 209 | + break; 210 | + } 211 | + } 212 | + 213 | + fclose(fileptr); 214 | + 215 | + free(device_path); 216 | + device_path = NULL; 217 | + 218 | + if (!found) 219 | + return -1; 220 | + 221 | + return 0; 222 | +} 223 | + 224 | +static int init_led(void) 225 | +{ 226 | + int led_driver_error = 0; 227 | + 228 | + led_driver_error |= validate_device(ledctrl_path, "pca9635"); 229 | + if (!led_driver_error) 230 | + { 231 | + led_driver_error |= configure_led_driver(ledctrl_path); 232 | + } 233 | + 234 | + return led_driver_error; 235 | +} 236 | + 237 | +//static unsigned char get_gamma(double value) 238 | +//{ 239 | +// return 0x80 | (int)(pow(value / 255.0, 2.5) * 127.0 + 0.5); 240 | +//} 241 | + 242 | +static void temp_to_led(int sensor, float tempC) 243 | +{ 244 | + //double step; 245 | + char tmp_buffer[64] = {0}; 246 | + unsigned char r,g,b; 247 | + float low, medium, high; 248 | + 249 | + low = 18.0; 250 | + medium = 22.0; 251 | + high = 26.0; 252 | + r = g = b = 0; 253 | + 254 | + 255 | + if (sensor != 1) 256 | + return; 257 | + 258 | + if (tempC < low) 259 | + { 260 | + b = 0xFF; 261 | + } 262 | + else if (tempC < medium) 263 | + { 264 | + //step = 0xFF * ((double)tempC - low)/(medium - low); 265 | + //g = get_gamma(step); 266 | + //b = get_gamma(0xFF - step); 267 | + g = 0xFF * (tempC - low)/(medium - low); 268 | + b = 0xFF - g; 269 | + } 270 | + else if (tempC < high) 271 | + { 272 | + //step = 0xFF * ((double)tempC - medium)/(high - medium); 273 | + //r = get_gamma(step); 274 | + //g = get_gamma(0xFF - step); 275 | + r = 0xFF * (tempC - medium)/(high - medium); 276 | + g = 0xFF - r; 277 | + } 278 | + else 279 | + { 280 | + r = 0xFF; 281 | + } 282 | + 283 | + sprintf(tmp_buffer, "0%02x1%02x2%02x300400500600700800900a00b00c00d00e00f00", r ,b, g); 284 | + set_led_driver_pattern(ledctrl_path, tmp_buffer); 285 | +} 286 | + 287 | +static void temp_turn_off_leds(void) 288 | +{ 289 | + set_led_driver_pattern(ledctrl_path, "000100200300400500600700800900a00b00c00d00e00f00"); 290 | +} 291 | + 292 | +/** Get and print the sensor values for a given device and sensor. */ 293 | +void read_device_sensor( tempered_device *device, int sensor, FILE *outfileptr ) 294 | +{ 295 | + if (outfileptr) 296 | + fprintf(outfileptr, "Sensor %i:", sensor); 297 | + printf( "Sensor %i:", sensor ); 298 | + int type = tempered_get_sensor_type( device, sensor ); 299 | + if ( type == TEMPERED_SENSOR_TYPE_NONE ) 300 | + { 301 | + if (outfileptr) 302 | + fprintf( outfileptr, " No such sensor, or type is not supported.\n" ); 303 | + printf( " No such sensor, or type is not supported.\n" ); 304 | + return; 305 | + } 306 | + if ( type & TEMPERED_SENSOR_TYPE_TEMPERATURE ) 307 | + { 308 | + float tempC; 309 | + if ( tempered_get_temperature( device, sensor, &tempC ) ) 310 | + { 311 | + temp_to_led(sensor, tempC); 312 | + if (outfileptr) 313 | + fprintf(outfileptr, " %.2f°C", tempC); 314 | + printf( " %.2f°C", tempC ); 315 | + } 316 | + else 317 | + { 318 | + if (outfileptr) 319 | + fprintf(outfileptr, 320 | + " temperature failed (%s)", 321 | + tempered_error( device ) 322 | + ); 323 | + printf( 324 | + " temperature failed (%s)", 325 | + tempered_error( device ) 326 | + ); 327 | + } 328 | + } 329 | + if ( type & TEMPERED_SENSOR_TYPE_HUMIDITY ) 330 | + { 331 | + float rel_hum; 332 | + if ( tempered_get_humidity( device, sensor, &rel_hum ) ) 333 | + { 334 | + if (outfileptr) 335 | + fprintf( outfileptr, " %.1f%%RH", rel_hum ); 336 | + printf( " %.1f%%RH", rel_hum ); 337 | + } 338 | + else 339 | + { 340 | + if (outfileptr) 341 | + fprintf(outfileptr, 342 | + " humidity failed (%s)", 343 | + tempered_error( device ) 344 | + ); 345 | + printf( 346 | + " humidity failed (%s)", 347 | + tempered_error( device ) 348 | + ); 349 | + } 350 | + } 351 | + if (outfileptr) 352 | + fprintf( outfileptr, "\n" ); 353 | + printf( "\n" ); 354 | +} 355 | + 356 | +/** Get and print the sensor values for a given device repeatedly. */ 357 | +void read_repeatedly( tempered_device *device ) 358 | +{ 359 | + while (!terminated) 360 | + { 361 | + if ( !tempered_read_sensors( device ) ) 362 | + { 363 | + printf( 364 | + "Failed to read the sensors: %s\n", 365 | + tempered_error( device ) 366 | + ); 367 | + } 368 | + else 369 | + { 370 | + FILE *outfileptr = NULL; 371 | + if (temperature_output_path != NULL) 372 | + outfileptr = fopen(temperature_output_path, "w"); 373 | + int sensor, sensors = tempered_get_sensor_count( device ); 374 | + for ( sensor = 0; sensor < sensors; sensor++ ) 375 | + { 376 | + read_device_sensor( device, sensor, outfileptr ); 377 | + } 378 | + if (outfileptr) 379 | + { 380 | + fclose(outfileptr); 381 | + outfileptr = NULL; 382 | + } 383 | + } 384 | + sleep(poll_rate); 385 | + } 386 | + temp_turn_off_leds(); 387 | +} 388 | + 389 | + 390 | +/** Open the device with the given device path. */ 391 | +tempered_device* open_device( const char *dev_path ) 392 | +{ 393 | + char *error = NULL; 394 | + struct tempered_device_list *list = tempered_enumerate( &error ); 395 | + if ( list == NULL ) 396 | + { 397 | + if ( error == NULL ) 398 | + { 399 | + printf( "No devices were found.\n" ); 400 | + } 401 | + else 402 | + { 403 | + fprintf( stderr, "Failed to enumerate devices: %s\n", error ); 404 | + free( error ); 405 | + } 406 | + return NULL; 407 | + } 408 | + tempered_device *device = NULL; 409 | + bool found = false; 410 | + struct tempered_device_list *dev; 411 | + for ( dev = list ; dev != NULL ; dev = dev->next ) 412 | + { 413 | + if ( strcmp( dev->path, dev_path ) == 0 ) 414 | + { 415 | + found = true; 416 | + device = tempered_open( dev, &error ); 417 | + break; 418 | + } 419 | + } 420 | + tempered_free_device_list( list ); 421 | + if ( device == NULL ) 422 | + { 423 | + if ( found ) 424 | + { 425 | + fprintf( 426 | + stderr, "Opening %s failed, error: %s\n", 427 | + dev_path, error 428 | + ); 429 | + free( error ); 430 | + } 431 | + else 432 | + { 433 | + fprintf( stderr, "Device not found: %s\n", dev_path ); 434 | + } 435 | + } 436 | + return device; 437 | +} 438 | + 439 | +static void syntax() 440 | +{ 441 | + fprintf(stderr, "Usage:\n"); 442 | + fprintf(stderr, "%s [options]\n", progname); 443 | + fprintf(stderr, "\n"); 444 | + fprintf(stderr, "options:\n"); 445 | + fprintf(stderr, " -i TEMPer device path (default %s)\n", temper_path); 446 | + fprintf(stderr, " -l PCA9635 LED device path (default %s)\n", ledctrl_path); 447 | + fprintf(stderr, " -o Output text file (default %s)\n", temperature_output_path); 448 | + fprintf(stderr, " -p Poll rate (default %d)\n", poll_rate); 449 | + fprintf(stderr, " -h Display this information\n"); 450 | + fprintf(stderr, "\n"); 451 | + exit(EXIT_FAILURE); 452 | +} 453 | + 454 | + 455 | +int main( int argc, char *argv[] ) 456 | +{ 457 | + int opt; 458 | + 459 | + progname = argv[0]; 460 | + 461 | + while ((opt = getopt (argc, argv, "i:l:o:p:h")) != -1) 462 | + { 463 | + switch (opt) 464 | + { 465 | + case 'i': temper_path = optarg; if (strlen(temper_path) == 0) syntax(); break; 466 | + case 'l': ledctrl_path = optarg; if (strlen(ledctrl_path) == 0) syntax(); break; 467 | + case 'o': temperature_output_path = optarg; if (strlen(temperature_output_path) == 0) temperature_output_path = NULL; break; 468 | + case 'p': poll_rate = atoi(optarg); if (poll_rate < 1) syntax(); break; 469 | + case 'h': // fall through 470 | + default: 471 | + syntax(); 472 | + break; 473 | + } 474 | + } 475 | + 476 | + if (temperature_output_path != NULL) 477 | + { 478 | + char *tmp = strdup(temperature_output_path); 479 | + char *dir = dirname(tmp); 480 | + mkdir(dir, 0755); 481 | + free(tmp); 482 | + } 483 | + 484 | + if (init_led() != 0) 485 | + { 486 | + fprintf( stderr, "Error: LED Control path not found: %s\n", ledctrl_path ); 487 | + exit(EXIT_FAILURE); 488 | + } 489 | + signal(SIGINT, handle_terminate_signal); 490 | + signal(SIGTERM, handle_terminate_signal); 491 | + 492 | + char *error = NULL; 493 | + if ( !tempered_init( &error ) ) 494 | + { 495 | + fprintf( stderr, "Failed to initialize libtempered: %s\n", error ); 496 | + free( error ); 497 | + exit(EXIT_FAILURE); 498 | + } 499 | + 500 | + tempered_device *device = open_device( temper_path ); 501 | + if ( device != NULL ) 502 | + { 503 | + read_repeatedly( device ); 504 | + tempered_close( device ); 505 | + } 506 | + 507 | + if ( !tempered_exit( &error ) ) 508 | + { 509 | + fprintf( stderr, "Failed to shut down libtempered: %s\n", error ); 510 | + free( error ); 511 | + exit(EXIT_FAILURE); 512 | + } 513 | + return 0; 514 | +} 515 | -------------------------------------------------------------------------------- /kernel/add-rpi-mbed.patch: -------------------------------------------------------------------------------- 1 | commit 37bf47fec503c760556d0f6da848849d7e7bf734 2 | Author: Joo Aun Saw 3 | Date: Fri Sep 19 16:49:09 2014 +1000 4 | 5 | Add rpi-mbed audio codec support with GPCLK0 patch. 6 | 7 | diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c 8 | index 85bd84f..02fd5ff 100644 9 | --- a/arch/arm/mach-bcm2708/bcm2708.c 10 | +++ b/arch/arm/mach-bcm2708/bcm2708.c 11 | @@ -637,6 +637,38 @@ static struct platform_device bcm2708_i2s_device = { 12 | }; 13 | #endif 14 | 15 | +#ifdef CONFIG_SND_BCM2708_SOC_RPI_CODEC_MBED_MODULE 16 | +static struct platform_device snd_rpi_mbed_device = { 17 | + .name = "snd-rpi-mbed", 18 | + .id = 0, 19 | + .num_resources = 0, 20 | +}; 21 | + 22 | +static struct i2c_board_info __initdata snd_rpi_mbed_i2c_devices[] = { 23 | + { 24 | + // LED driver 25 | + I2C_BOARD_INFO("pca9635", 0x07), 26 | + }, 27 | + { 28 | + I2C_BOARD_INFO("tlv320aic23", 0x1b) 29 | + }, 30 | +}; 31 | +#endif 32 | + 33 | +#ifdef CONFIG_SND_BCM2708_SOC_RPI_CODEC_TDA1541A_MODULE 34 | +static struct platform_device snd_rpi_tda1541a_device = { 35 | + .name = "snd-rpi-tda1541a", 36 | + .id = 0, 37 | + .num_resources = 0, 38 | +}; 39 | + 40 | +static struct platform_device snd_rpi_tda1541a_codec_device = { 41 | + .name = "tda1541a-codec", 42 | + .id = -1, 43 | + .num_resources = 0, 44 | +}; 45 | +#endif 46 | + 47 | #if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC_MODULE) 48 | static struct platform_device snd_hifiberry_dac_device = { 49 | .name = "snd-hifiberry-dac", 50 | @@ -837,6 +869,11 @@ void __init bcm2708_init(void) 51 | bcm_register_device(&bcm2708_i2s_device); 52 | #endif 53 | 54 | +#ifdef CONFIG_SND_BCM2708_SOC_RPI_CODEC_MBED_MODULE 55 | + bcm_register_device(&snd_rpi_mbed_device); 56 | + i2c_register_board_info(1, snd_rpi_mbed_i2c_devices, ARRAY_SIZE(snd_rpi_mbed_i2c_devices)); 57 | +#endif 58 | + 59 | #if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC_MODULE) 60 | bcm_register_device(&snd_hifiberry_dac_device); 61 | bcm_register_device(&snd_pcm5102a_codec_device); 62 | diff --git a/arch/arm/mach-bcm2708/include/mach/platform.h b/arch/arm/mach-bcm2708/include/mach/platform.h 63 | index 2e7e1bb..2921485 100644 64 | --- a/arch/arm/mach-bcm2708/include/mach/platform.h 65 | +++ b/arch/arm/mach-bcm2708/include/mach/platform.h 66 | @@ -62,6 +62,7 @@ 67 | #define DMA_BASE (BCM2708_PERI_BASE + 0x7000) /* DMA controller */ 68 | #define ARM_BASE (BCM2708_PERI_BASE + 0xB000) /* BCM2708 ARM control block */ 69 | #define PM_BASE (BCM2708_PERI_BASE + 0x100000) /* Power Management, Reset controller and Watchdog registers */ 70 | +#define GPCLK_BASE (BCM2708_PERI_BASE + 0x101000) /* General Purpose Clock */ 71 | #define PCM_CLOCK_BASE (BCM2708_PERI_BASE + 0x101098) /* PCM Clock */ 72 | #define RNG_BASE (BCM2708_PERI_BASE + 0x104000) /* Hardware RNG */ 73 | #define GPIO_BASE (BCM2708_PERI_BASE + 0x200000) /* GPIO */ 74 | diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig 75 | index 073035b..2c1f1f2 100644 76 | --- a/sound/soc/bcm/Kconfig 77 | +++ b/sound/soc/bcm/Kconfig 78 | @@ -9,6 +9,13 @@ config SND_BCM2708_SOC_I2S 79 | the BCM2708 I2S interface. You will also need 80 | to select the audio interfaces to support below. 81 | 82 | +config SND_BCM2708_SOC_RPI_CODEC_MBED 83 | + tristate "Support mbed AudioCODEC (TLV320AIC23B)" 84 | + depends on SND_BCM2708_SOC_I2S 85 | + select SND_SOC_TLV320AIC23 86 | + help 87 | + Say Y or M if you want to add support for mbed AudioCODEC (TLV320AIC23B). 88 | + 89 | config SND_BCM2708_SOC_HIFIBERRY_DAC 90 | tristate "Support for HifiBerry DAC" 91 | depends on SND_BCM2708_SOC_I2S 92 | diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile 93 | index 8b5e8bb..77a2090 100644 94 | --- a/sound/soc/bcm/Makefile 95 | +++ b/sound/soc/bcm/Makefile 96 | @@ -4,6 +4,7 @@ snd-soc-bcm2708-i2s-objs := bcm2708-i2s.o 97 | obj-$(CONFIG_SND_BCM2708_SOC_I2S) += snd-soc-bcm2708-i2s.o 98 | 99 | # BCM2708 Machine Support 100 | +snd-soc-rpi-mbed-objs := rpi-mbed.o 101 | snd-soc-hifiberry-dac-objs := hifiberry_dac.o 102 | snd-soc-hifiberry-dacplus-objs := hifiberry_dacplus.o 103 | snd-soc-hifiberry-digi-objs := hifiberry_digi.o 104 | @@ -11,6 +12,7 @@ snd-soc-hifiberry-amp-objs := hifiberry_amp.o 105 | snd-soc-rpi-dac-objs := rpi-dac.o 106 | snd-soc-iqaudio-dac-objs := iqaudio-dac.o 107 | 108 | +obj-$(CONFIG_SND_BCM2708_SOC_RPI_CODEC_MBED) += snd-soc-rpi-mbed.o 109 | obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) += snd-soc-hifiberry-dac.o 110 | obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS) += snd-soc-hifiberry-dacplus.o 111 | obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) += snd-soc-hifiberry-digi.o 112 | diff --git a/sound/soc/bcm/bcm2708-i2s.c b/sound/soc/bcm/bcm2708-i2s.c 113 | index 905f076..c08c5a8 100644 114 | --- a/sound/soc/bcm/bcm2708-i2s.c 115 | +++ b/sound/soc/bcm/bcm2708-i2s.c 116 | @@ -50,6 +50,13 @@ 117 | 118 | #include 119 | 120 | +#define BCM2708_I2S_SYNC_TO_GPCLK0 121 | + 122 | +#ifdef BCM2708_I2S_SYNC_TO_GPCLK0 123 | +#define BCM2708_GPCLK_CTL_REG (0x70/4) 124 | +#define BCM2708_GPCLK_DIV_REG (0x74/4) 125 | +#endif 126 | + 127 | /* Clock registers */ 128 | #define BCM2708_CLK_PCMCTL_REG 0x00 129 | #define BCM2708_CLK_PCMDIV_REG 0x04 130 | @@ -66,6 +73,7 @@ 131 | 132 | #define BCM2708_CLK_SHIFT (12) 133 | #define BCM2708_CLK_DIVI(v) ((v) << BCM2708_CLK_SHIFT) 134 | +#define BCM2708_CLK_DIVI_MASK (0xFFF000) 135 | #define BCM2708_CLK_DIVF(v) (v) 136 | #define BCM2708_CLK_DIVF_MASK (0xFFF) 137 | 138 | @@ -454,6 +462,43 @@ static int bcm2708_i2s_hw_params(struct snd_pcm_substream *substream, 139 | clk_src = BCM2708_CLK_SRC_OSC; 140 | mash = BCM2708_CLK_MASH_0; 141 | 142 | +#ifdef BCM2708_I2S_SYNC_TO_GPCLK0 143 | + { 144 | + unsigned int *gpclk_map; 145 | + uint64_t gpclk_dividend; 146 | + uint64_t i2s_dividend; 147 | + 148 | + (void)frame_master; 149 | + (void)bit_master; 150 | + 151 | + if (!dev->bclk_ratio) { 152 | + /* 153 | + * Overwrite bclk_ratio, because the 154 | + * above trick is not needed or can 155 | + * not be used. 156 | + */ 157 | + bclk_ratio = 2 * data_length; 158 | + } 159 | + 160 | + target_frequency = sampling_rate * bclk_ratio; 161 | + 162 | + clk_src = BCM2708_CLK_SRC_PLLD; 163 | + mash = BCM2708_CLK_MASH_1; 164 | + 165 | + i2s_dividend = bcm2708_clk_freq[clk_src]; 166 | + i2s_dividend <<= BCM2708_CLK_SHIFT; 167 | + do_div(i2s_dividend, target_frequency); 168 | + 169 | + /* Work out an interger ratio so both clocks are in sync */ 170 | + gpclk_map = ioremap(GPCLK_BASE, SZ_4K); 171 | + gpclk_dividend = *(gpclk_map + BCM2708_GPCLK_DIV_REG); 172 | + i2s_dividend += (gpclk_dividend >> 1); 173 | + do_div(i2s_dividend, gpclk_dividend); 174 | + i2s_dividend *= gpclk_dividend; 175 | + divi = i2s_dividend >> BCM2708_CLK_SHIFT; 176 | + divf = i2s_dividend & BCM2708_CLK_DIVF_MASK; 177 | + } 178 | +#else 179 | if (bcm2708_clk_freq[clk_src] % target_frequency == 0 180 | && bit_master && frame_master) { 181 | divi = bcm2708_clk_freq[clk_src] / target_frequency; 182 | @@ -481,6 +526,7 @@ static int bcm2708_i2s_hw_params(struct snd_pcm_substream *substream, 183 | divi = dividend >> BCM2708_CLK_SHIFT; 184 | divf = dividend & BCM2708_CLK_DIVF_MASK; 185 | } 186 | +#endif 187 | 188 | /* Set clock divider */ 189 | regmap_write(dev->clk_regmap, BCM2708_CLK_PCMDIV_REG, BCM2708_CLK_PASSWD 190 | diff --git a/sound/soc/bcm/rpi-mbed.c b/sound/soc/bcm/rpi-mbed.c 191 | new file mode 100644 192 | index 0000000..3f84403 193 | --- /dev/null 194 | +++ b/sound/soc/bcm/rpi-mbed.c 195 | @@ -0,0 +1,259 @@ 196 | +/* 197 | + * ASoC driver for mbed AudioCODEC (with a TLV320AIC23b) 198 | + * connected to a Raspberry Pi 199 | + * 200 | + * Author: Florian Meier, 201 | + * Copyright 2013 202 | + * 203 | + * This program is free software; you can redistribute it and/or modify 204 | + * it under the terms of the GNU General Public License version 2 as 205 | + * published by the Free Software Foundation. 206 | + */ 207 | + 208 | +#include 209 | +#include 210 | +#include 211 | + 212 | +#include 213 | +#include 214 | +#include 215 | +#include 216 | + 217 | +#include "../codecs/tlv320aic23.h" 218 | + 219 | +#define MBED_CODEC_MCLK 12288000 220 | + 221 | +/* GP Clock pin */ 222 | +#define BCM2708_GPCLK0_GPIO_PIN4 4 223 | +#define BCM2708_GPCLK0_GPIO_PIN4_ALT 0 224 | + 225 | +/* GP Clock registers */ 226 | +#define BCM2708_GPCLK_CTL_REG (0x70/4) 227 | +#define BCM2708_GPCLK_DIV_REG (0x74/4) 228 | + 229 | +/* Clock register settings */ 230 | +#define BCM2708_CLK_PASSWD (0x5a000000) 231 | +#define BCM2708_CLK_PASSWD_MASK (0xff000000) 232 | +#define BCM2708_CLK_MASH(v) ((v) << 9) 233 | +#define BCM2708_CLK_FLIP BIT(8) 234 | +#define BCM2708_CLK_BUSY BIT(7) 235 | +#define BCM2708_CLK_KILL BIT(5) 236 | +#define BCM2708_CLK_ENAB BIT(4) 237 | +#define BCM2708_CLK_SRC(v) (v) 238 | + 239 | +#define BCM2708_CLK_SHIFT (12) 240 | +#define BCM2708_CLK_DIVI(v) ((v) << BCM2708_CLK_SHIFT) 241 | +#define BCM2708_CLK_DIVF(v) (v) 242 | +#define BCM2708_CLK_DIVF_MASK (0xFFF) 243 | + 244 | +enum { 245 | + BCM2708_CLK_MASH_0 = 0, 246 | + BCM2708_CLK_MASH_1, 247 | + BCM2708_CLK_MASH_2, 248 | + BCM2708_CLK_MASH_3, 249 | +}; 250 | + 251 | +enum { 252 | + BCM2708_CLK_SRC_GND = 0, 253 | + BCM2708_CLK_SRC_OSC, 254 | + BCM2708_CLK_SRC_DBG0, 255 | + BCM2708_CLK_SRC_DBG1, 256 | + BCM2708_CLK_SRC_PLLA, 257 | + BCM2708_CLK_SRC_PLLC, 258 | + BCM2708_CLK_SRC_PLLD, 259 | + BCM2708_CLK_SRC_HDMI, 260 | +}; 261 | + 262 | +/* Most clocks are not useable (freq = 0) */ 263 | +static const unsigned int bcm2708_clk_freq[BCM2708_CLK_SRC_HDMI+1] = { 264 | + [BCM2708_CLK_SRC_GND] = 0, 265 | + [BCM2708_CLK_SRC_OSC] = 19200000, 266 | + [BCM2708_CLK_SRC_DBG0] = 0, 267 | + [BCM2708_CLK_SRC_DBG1] = 0, 268 | + [BCM2708_CLK_SRC_PLLA] = 0, 269 | + [BCM2708_CLK_SRC_PLLC] = 0, 270 | + [BCM2708_CLK_SRC_PLLD] = 500000000, 271 | + [BCM2708_CLK_SRC_HDMI] = 0, 272 | +}; 273 | + 274 | +static void snd_rpi_mbed_setup_gpio(void) 275 | +{ 276 | + /* 277 | + * This is the common way to handle the GPIO pins for 278 | + * the Raspberry Pi. 279 | + * TODO Better way would be to handle 280 | + * this in the device tree! 281 | + */ 282 | +#define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3)) 283 | +#define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3)) 284 | + 285 | + unsigned int *gpio; 286 | + int pin,alt; 287 | + 288 | + gpio = ioremap(GPIO_BASE, SZ_16K); 289 | + 290 | + pin=BCM2708_GPCLK0_GPIO_PIN4; 291 | + alt=BCM2708_GPCLK0_GPIO_PIN4_ALT; 292 | + 293 | + /* configure GPCLK0 pin to correct ALT mode */ 294 | + INP_GPIO(pin); /* set mode to GPIO input first */ 295 | + SET_GPIO_ALT(pin, alt); /* set mode to ALT */ 296 | + 297 | +#undef INP_GPIO 298 | +#undef SET_GPIO_ALT 299 | +} 300 | + 301 | +#define GP_CLK0_CTL *(clk_map + BCM2708_GPCLK_CTL_REG) 302 | +#define GP_CLK0_DIV *(clk_map + BCM2708_GPCLK_DIV_REG) 303 | + 304 | +static void snd_rpi_mbed_start_gpclk(unsigned int *clk_map) 305 | +{ 306 | + unsigned int tmp; 307 | + 308 | + tmp = GP_CLK0_CTL; 309 | + tmp &= ~(BCM2708_CLK_PASSWD_MASK | BCM2708_CLK_ENAB); 310 | + tmp |= (BCM2708_CLK_PASSWD | BCM2708_CLK_ENAB); 311 | + GP_CLK0_CTL = tmp; 312 | +} 313 | + 314 | +static void snd_rpi_mbed_stop_gpclk(unsigned int *clk_map) 315 | +{ 316 | + int timeout = 1000; 317 | + unsigned int tmp; 318 | + 319 | + /* Stop clock */ 320 | + tmp = GP_CLK0_CTL; 321 | + tmp &= ~(BCM2708_CLK_PASSWD_MASK | BCM2708_CLK_ENAB); 322 | + tmp |= BCM2708_CLK_PASSWD; 323 | + GP_CLK0_CTL = tmp; 324 | + 325 | + /* Wait for the BUSY flag going down */ 326 | + while (--timeout) { 327 | + if (!(GP_CLK0_DIV & BCM2708_CLK_BUSY)) 328 | + break; 329 | + } 330 | + 331 | + if (!timeout) { 332 | + /* KILL the clock */ 333 | + tmp = GP_CLK0_CTL; 334 | + tmp &= ~(BCM2708_CLK_KILL | BCM2708_CLK_PASSWD_MASK); 335 | + tmp |= (BCM2708_CLK_KILL | BCM2708_CLK_PASSWD); 336 | + GP_CLK0_CTL = tmp; 337 | + } 338 | +} 339 | + 340 | +static void snd_rpi_mbed_setup_gpclk(void) 341 | +{ 342 | + unsigned int mash = BCM2708_CLK_MASH_1; 343 | + unsigned int divi, divf, target_frequency; 344 | + int clk_src = BCM2708_CLK_SRC_PLLD; 345 | + unsigned int *clk_map; 346 | + uint64_t dividend; 347 | + 348 | + clk_map = ioremap(GPCLK_BASE, SZ_4K); 349 | + 350 | + snd_rpi_mbed_stop_gpclk(clk_map); 351 | + 352 | + target_frequency = MBED_CODEC_MCLK; 353 | + 354 | + dividend = bcm2708_clk_freq[clk_src]; 355 | + dividend <<= BCM2708_CLK_SHIFT; 356 | + do_div(dividend, target_frequency); 357 | + divi = dividend >> BCM2708_CLK_SHIFT; 358 | + divf = dividend & BCM2708_CLK_DIVF_MASK; 359 | + 360 | + /* Set clock divider */ 361 | + GP_CLK0_DIV = BCM2708_CLK_PASSWD | BCM2708_CLK_DIVI(divi) | BCM2708_CLK_DIVF(divf); 362 | + 363 | + /* Setup clock, but don't start it yet */ 364 | + GP_CLK0_CTL = BCM2708_CLK_PASSWD | BCM2708_CLK_MASH(mash) | BCM2708_CLK_SRC(clk_src); 365 | + 366 | + snd_rpi_mbed_start_gpclk(clk_map); 367 | +} 368 | + 369 | +static int snd_rpi_mbed_init(struct snd_soc_pcm_runtime *rtd) 370 | +{ 371 | + snd_rpi_mbed_setup_gpclk(); 372 | + snd_rpi_mbed_setup_gpio(); 373 | + return 0; 374 | +} 375 | + 376 | +static int snd_rpi_mbed_hw_params(struct snd_pcm_substream *substream, 377 | + struct snd_pcm_hw_params *params) 378 | +{ 379 | + struct snd_soc_pcm_runtime *rtd = substream->private_data; 380 | + struct snd_soc_dai *codec_dai = rtd->codec_dai; 381 | + int sysclk; 382 | + 383 | + sysclk = MBED_CODEC_MCLK; /* this is clocked by GPCLK0 */ 384 | + 385 | + /* set tlv320aic23 sysclk */ 386 | + snd_soc_dai_set_sysclk(codec_dai, 0, sysclk, 0); 387 | + 388 | + /* configure tlv320aic23 i2s mode */ 389 | + snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_MASTER_MASK | SND_SOC_DAIFMT_FORMAT_MASK); 390 | + 391 | + return 0; 392 | +} 393 | + 394 | +/* machine stream operations */ 395 | +static struct snd_soc_ops snd_rpi_mbed_ops = { 396 | + .hw_params = snd_rpi_mbed_hw_params, 397 | +}; 398 | + 399 | +static struct snd_soc_dai_link snd_rpi_mbed_dai[] = { 400 | +{ 401 | + .name = "TLV320AIC23", 402 | + .stream_name = "TLV320AIC23 HiFi", 403 | + .cpu_dai_name = "bcm2708-i2s.0", 404 | + .codec_dai_name = "tlv320aic23-hifi", 405 | + .platform_name = "bcm2708-i2s.0", 406 | + .codec_name = "tlv320aic23-codec.1-001b", 407 | + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 408 | + SND_SOC_DAIFMT_CBS_CFS, 409 | + .ops = &snd_rpi_mbed_ops, 410 | + .init = snd_rpi_mbed_init, 411 | +}, 412 | +}; 413 | + 414 | +/* audio machine driver */ 415 | +static struct snd_soc_card snd_rpi_mbed = { 416 | + .name = "snd_rpi_mbed", 417 | + .dai_link = snd_rpi_mbed_dai, 418 | + .num_links = ARRAY_SIZE(snd_rpi_mbed_dai), 419 | +}; 420 | + 421 | +static int snd_rpi_mbed_probe(struct platform_device *pdev) 422 | +{ 423 | + int ret = 0; 424 | + 425 | + snd_rpi_mbed.dev = &pdev->dev; 426 | + ret = snd_soc_register_card(&snd_rpi_mbed); 427 | + if (ret) { 428 | + dev_err(&pdev->dev, 429 | + "snd_soc_register_card() failed: %d\n", ret); 430 | + } 431 | + 432 | + return ret; 433 | +} 434 | + 435 | + 436 | +static int snd_rpi_mbed_remove(struct platform_device *pdev) 437 | +{ 438 | + return snd_soc_unregister_card(&snd_rpi_mbed); 439 | +} 440 | + 441 | +static struct platform_driver snd_rpi_mbed_driver = { 442 | + .driver = { 443 | + .name = "snd-rpi-mbed", 444 | + .owner = THIS_MODULE, 445 | + }, 446 | + .probe = snd_rpi_mbed_probe, 447 | + .remove = snd_rpi_mbed_remove, 448 | +}; 449 | + 450 | +module_platform_driver(snd_rpi_mbed_driver); 451 | + 452 | +MODULE_AUTHOR("Florian Meier"); 453 | +MODULE_DESCRIPTION("ASoC Driver for Raspberry Pi connected to mbed AudioCODEC"); 454 | +MODULE_LICENSE("GPL"); 455 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | 341 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | bbPiCam 2 | ======= 3 | 4 | Raspberry Pi Baby Monitor. 5 | 6 | Features: 7 | * True day and night vision. 8 | * 1280 x 960 HD video quality. 9 | * RGB LED shows temperature at a quick glance. 10 | * RTSP & RTMP audio video stream (playable on web browsers and Android devices). 11 | 12 | ![Image of bbPiCam](https://github.com/jasaw/bbPiCam/blob/master/docs/bbPiCam_mini.jpg) 13 | 14 | ![Day view](https://github.com/jasaw/bbPiCam/blob/master/docs/day.jpg) 15 | 16 | ![Night view](https://github.com/jasaw/bbPiCam/blob/master/docs/night.jpg) 17 | 18 | # Hardware 19 | 20 | Setting up the hardware requires a fair bit of soldering and drilling. Let's get started ... 21 | 22 | ### Bill of Materials 23 | 24 | * Raspberry Pi Model B (rev 2) 25 | * SD card 26 | * Pi NoIR camera board - http://www.adafruit.com/products/1567 27 | * AudioCODEC for mbed - RS Components part number: 754-1974 28 | * RDing TEMPer2 Temperature sensor 29 | * Diffused RGB (tri-color) LED - http://www.adafruit.com/products/159 30 | * 5V 1A DC voltage regulator (e.g. LM7805) or DC-DC converter 31 | * Electret Microphone Amplifier - MAX9814 with Auto Gain Control - http://www.adafruit.com/products/1713 32 | * CS Mount Dual IR-Cut Optical Filter for CCTV CMOS Board Camera - http://www.camera2000.com/en/cs-mount-dual-ir-cut-optical-filter-for-cctv-cmos-board-camera.html 33 | * IR Light Board for CCTV Camera Housing - http://www.camera2000.com/en/24-leds-45deg-25m-view-ir-light-board-for-dia-60mm-camera-housing.html 34 | * PCA9635 LED controller 35 | * 12V DC 1A regulated power adaptor 36 | * Panel Mount Tactile Pushbutton 37 | * Panel Mount 2.5mm Bulkhead Male DC Power Connector 38 | * Enclosure 39 | * Heatshrink 40 | 41 | ### Pi NoIR Camera, IR Light and IR-Cut Controller 42 | 43 | There are 3 headers on the IR-Cut Controller board: 44 | 45 | **J1** (Power input): 46 | - pin 1: 12V 47 | - pin 2: GND 48 | 49 | **J2** (Power output): 50 | - pin 1: 12V 51 | - pin 2: GND 52 | - pin 3: Day/Night control signal input (solder to photovaristor switch IC output of the IR board) 53 | - pin 4: Standard power level output (under IR-CUT condition) 54 | 55 | **J3** (IR-CUT connection) 56 | 57 | Pin 3 of J2 (yellow wire) needs to be soldered to the output of the 3-pin photovaristor switch IC on the IR Light board. I recommend soldering it via a 1-pin connector so it can be disconnected when mounting to an enclosure. 58 | 59 | Connect the J2 12V output connector (pin 1 & 2) to the IR Light board 12V input. **Warning:** My 12V connector had the wrong polarity. Make sure you check it and switch the wires on the connector if required. 60 | 61 | Remove the protective tapes on the Pi camera and IR filter. 62 | 63 | Now we just need to line up the 3 components this way: 64 | Pi Camera --- 3mm Spacer --- IR Filter --- Spacer --- IR Light Board 65 | 66 | The IR LEDs emit IR light mostly at the front, but some light are reflected back through the LED plastic case. This reflection is strong enough to interfere with the Pi camera if you don't have a something to block the light from the sides. I used heatshrink on the IR LEDs to stop the light reflection. 67 | 68 | ### I2S Audio Codec 69 | 70 | A reliable way to enable audio input on the Pi is via the I2S interface. I2S is short for Inter-IC Sound. Only the Rev 2 and Model B+ Pi expose the I2S signals. 71 | 72 | The I2S signals are exposed via P5 header, next to the P1 header. Solder a header on it. Refer to [RPi Low-level peripherals](http://elinux.org/RPi_Low-level_peripherals) for pinouts. 73 | 74 | As for the audio codec, I used the mbed AudioCodec board based on TI TLV320AIC23B. I had to make a few modifications to the board, which may not suit everyone. Those who are not comfortable with soldering can explore other codecs like the PROTO audio codec board based on WM8731. This [I2S guide](http://blog.koalo.de/2013/05/i2s-support-for-raspberry-pi.html) by Koalo may be helpful. 75 | 76 | Modifications made to the mbed AudioCodec board: 77 | * MIC Bias (pin 17) and MIC Input (pin 18) of the IC are not exposed on a connector, so I soldered wires from the MIC BIAS and MIC IN pins to the unused header. I also soldered a wire from the GND via to the unused header. 78 | 79 | ![mbed audio codec mod](https://github.com/jasaw/bbPiCam/blob/master/docs/mbed-codec-mods.jpg) 80 | 81 | * The 12MHz crystal was removed and MCLK (pin 25) and wires soldered from the MCLK pin to an unused header pin. 82 | 83 | The crystal was removed so GPCLK0 signal can be fed as the MCLK to ensure MCLK and I2S BCLK are synchronous. This was done after I discovered both clocks were drifting, causing clicks on the 1st channel of the audio input. I did not manage to remove the clicking noise completely, so I ended up configuring ffmpeg to only use channel 2 and output mono audio. 84 | 85 | Connection between the mbed AudioCodec and the Raspberry Pi B: 86 | 87 | ``` 88 | mbed AudioCodec | Raspberry Pi 89 | ----------------- +--------------------- 90 | BCLK (I2S) | P5 - 03 91 | CS | 3V3 92 | DIN (I2S) | P5 - 06 93 | DOUT (I2S) | P5 - 05 94 | LRCOUT (I2S) | P5 - 04 95 | MODE | GND 96 | | 97 | SCLK (I2C) | P1 - 05 98 | SDIN (I2C) | P1 - 03 99 | | 100 | MCLK | GPCLK0 101 | ``` 102 | 103 | If using Raspberry Pi B+ or Pi2 B: 104 | 105 | ``` 106 | mbed AudioCodec | Raspberry Pi 107 | ----------------- +--------------------- 108 | BCLK (I2S) | J8 - 12 109 | CS | 3V3 110 | DIN (I2S) | J8 - 40 111 | DOUT (I2S) | J8 - 38 112 | LRCOUT (I2S) | J8 - 35 113 | MODE | GND 114 | | 115 | SCLK (I2C) | P1 - 05 116 | SDIN (I2C) | P1 - 03 117 | | 118 | MCLK | GPCLK0 119 | ``` 120 | 121 | The 4 I2S wires and MCLK wire have to be kept as short as possible to reduce electrical interference as they will be carrying high speed signals. 122 | 123 | ![mbed AudioCodec I2S Connection](https://github.com/jasaw/bbPiCam/blob/master/docs/mbed_audio_codec_i2s.jpg) 124 | 125 | The I2C pins are used for configuring the codec. 126 | 127 | ### MIC Input 128 | 129 | I made my own mic front-end circuit, but I recommend one with automatic gain. 130 | 131 | You can use this mic resistor network but the input gain is not high enough even with the 20dB gain enabled on the TLV320AIC23B. 132 | 133 | ![mic resistor network](https://github.com/jasaw/bbPiCam/blob/master/docs/mic_circuit.png) 134 | 135 | The components in the grey box can be replaced with a mic pre-amp circuit with automatic gain control. 136 | 137 | Adjust the R1 resistor to set the gain. 138 | 139 | ### RGB Temperature LED 140 | 141 | RGB LED is controlled by PCA9635 driver. There are other more suitable LED driver alternatives. I chose this part because I have left over from another project. 142 | 143 | ![mic resistor network](https://github.com/jasaw/bbPiCam/blob/master/docs/pca9635.png) 144 | 145 | ### Shutdown Button 146 | 147 | The "Shutdown" button does the obvious, shuts down the device when pressed. 148 | 149 | Button is connected to P1-11 and pulled up to 3.3V via 10K resistor. 150 | 151 | ![shutdown button](https://github.com/jasaw/bbPiCam/blob/master/docs/reset_button.png) 152 | 153 | # Software 154 | 155 | This guide only applies to Raspberry Pi B. 156 | 157 | Software modifications I've done so far: 158 | * I2S and rpi_mbed drivers to use GPCLK0 as MCLK. 159 | * Reduced the ffmpeg's default max_interleave_delta value. By default, ffmpeg buffers both audio and video stream in attempt to synchronize them, but ffmpeg is unable to synchronize the streams because raspivid video output does not contain timestamp information. By reducing the max_interleave_delta value, the de-sync between audio and video can be reduced. The only problem is, setting max_interleave_delta from command line did not do anything, so I just changed the default value as a quick workaround. 160 | 161 | Before we start, you'll need a Linux based Raspberry Pi Operating System. I strongly recommend Raspbian unless you know what you are doing. I use Raspbian Wheezy 2014-06-20. 162 | 163 | _If you're using model B+ or 2 B, I recommend the latest Raspbian Jessie image with device-tree enabled. You can load the I2S driver by adding dtoverlay=i2s-mmap to /boot/config.txt. I haven't got time to migrate this project to the Pi2, so you'll have to figure the rest out yourself. Sorry..._ 164 | 165 | While waiting for the Raspbian installation onto an SD card, we download the required software on a PC. 166 | 167 | ``` 168 | git clone https://github.com/jasaw/bbPiCam.git 169 | cd bbPiCam 170 | git submodule init 171 | git submodule update 172 | ``` 173 | 174 | This is going to take a while, so go do other things and come back later ... 175 | 176 | Now apply patches. 177 | 178 | ``` 179 | patch -p1 -d kernel/linux < kernel/add-rpi-mbed.patch 180 | patch -p1 -d kernel/linux < kernel/add-leds-pca9635.patch 181 | patch -p1 -d programs/ffmpeg < programs/ffmpeg-reduce-max-interleave-delta.patch 182 | patch -p1 -d programs/TEMPered < programs/TEMPered-fix-broken-cmakelists.patch 183 | patch -p1 -d programs/TEMPered < programs/TEMPered-add-temper2led.patch 184 | ``` 185 | 186 | ### Configure the Pi 187 | 188 | Enable I2C and Camera on the Pi. On the Pi, run: 189 | 190 | ``` 191 | sudo raspi-config 192 | ``` 193 | 194 | ### Install build tools 195 | ``` 196 | sudo apt-get install build-essential 197 | ``` 198 | 199 | ### Kernel and Drivers 200 | 201 | The kernel needs to be configured and cross compiled, then transfered to the Pi. 202 | 203 | Change into the kernel directory and set things up: 204 | ``` 205 | cd kernel 206 | mkdir modules 207 | ``` 208 | 209 | **Important**: Edit the build_env file to reflect your directory structure. 210 | 211 | Load environment variables: 212 | ``` 213 | . build_env 214 | ``` 215 | 216 | Clean up 217 | ``` 218 | cd linux 219 | make mrproper 220 | ``` 221 | 222 | Get kernel config from Pi running Raspbian. On the Pi, run: 223 | ``` 224 | zcat /proc/config.gz > /tmp/.config 225 | ``` 226 | 227 | Copy .config file to linux directory: 228 | ``` 229 | scp pi@rpi-cam:/tmp/.config ./.config 230 | ``` 231 | 232 | Restore the kernel config, then configure it: 233 | ``` 234 | make oldconfig 235 | make menuconfig 236 | ``` 237 | 238 | Enable rpi_mbed under: 239 | ``` 240 | Device Drivers 241 | > Sound card support 242 | > Advanced Linux Sound Architecture 243 | > ALSA for SoC audio support 244 | > SoC Audio support for the Broadcom BCM2708 I2S module 245 | ``` 246 | 247 | Enable PCA9635 LED driver: 248 | ``` 249 | Device Drivers 250 | > LED support 251 | > LED support for PCA9635 I2C chip 252 | ``` 253 | 254 | Compile kernel 255 | ``` 256 | make -j4 257 | ``` 258 | 259 | Install kernel modules 260 | ``` 261 | make modules_install 262 | ``` 263 | 264 | Create kernel.img from zImage (for Pi Model A & B) 265 | ``` 266 | cd ../tools/mkimage 267 | ./imagetool-uncompressed.py ${KERNEL_SRC}/arch/arm/boot/zImage 268 | cd ../.. 269 | ``` 270 | 271 | Create kernel7.img from zImage (for Pi Model A+, B+, 2 B) 272 | ``` 273 | cd ../tools/mkimage 274 | ./mkknlimg ${KERNEL_SRC}/arch/arm/boot/zImage kernel7.img 275 | cd ../.. 276 | ``` 277 | 278 | Remove symlinks in modules directory: 279 | ``` 280 | rm modules/lib/modules/3.12.28/build modules/lib/modules/3.12.28/source 281 | tar czf modules.tar.gz modules/ 282 | ``` 283 | 284 | Copy kernel.img and modules to the Pi 285 | ``` 286 | scp tools/mkimage/kernel.img pi@rpi-cam:/tmp 287 | scp modules.tar.gz pi@rpi-cam:/tmp 288 | ``` 289 | 290 | Replace the kernel on the Pi. On the Pi, run: 291 | ``` 292 | cd /boot 293 | sudo mv kernel.img kernel.img.org 294 | sudo mv /tmp/kernel.img . 295 | cd /tmp 296 | tar xzf modules.tar.gz 297 | cd /lib 298 | sudo mv modules modules_org 299 | sudo mv /tmp/modules/lib/modules /lib 300 | sudo chown -R root:root /lib/modules 301 | ``` 302 | 303 | add below lines to /etc/modules 304 | ``` 305 | snd_soc_bcm2708 306 | snd_soc_bcm2708_i2s 307 | bcm2708_dmaengine 308 | snd_soc_tlv320aic23 309 | snd_soc_rpi_mbed 310 | ``` 311 | 312 | Reboot the Pi 313 | ``` 314 | sudo reboot 315 | ``` 316 | 317 | If your Pi survived the reboot, you should be able to see the audio card: 318 | ``` 319 | arecord -L 320 | ``` 321 | 322 | If you can't see the audio card, try probing it on the I2C bus: 323 | ``` 324 | sudo modprobe i2c-dev 325 | sudo apt-get install i2c-tools 326 | sudo i2cdetect 1 327 | ``` 328 | It should have I2C address of 0x1b. 329 | 330 | Test to see if it works: 331 | ``` 332 | alsamixer -c 1 333 | arecord -D hw:1,0 -f DAT -r 8 /tmp/my_record.wav 334 | ``` 335 | 336 | The audio card can also be configured this way: 337 | ``` 338 | amixer -c 1 sset 'Mic' cap 339 | amixer -c 1 sset 'Mic Input' on 340 | amixer -c 1 sset 'Mic Booster' on 341 | ``` 342 | 343 | ### Libraries and Dependencies 344 | 345 | We need to set up libraries and dependencies on the Pi. On the Pi, run ... 346 | 347 | Install tools and libraries. 348 | ``` 349 | sudo apt-get install build-essential 350 | sudo apt-get install autotools-dev autoconf automake libtool 351 | sudo apt-get install libasound2-dev 352 | ``` 353 | 354 | **Note**: All libraries and tools can be cross compiled on a PC and transfered to the Pi. For simplicity, we compile them on the Pi. 355 | 356 | Build and install x264 library. 357 | ``` 358 | git clone git://git.videolan.org/x264 359 | cd x264 360 | ./configure --disable-asm --enable-shared 361 | make 362 | sudo make install 363 | ``` 364 | 365 | Build and install lame encoder. 366 | ``` 367 | wget http://downloads.sourceforge.net/project/lame/lame/3.99/lame-3.99.5.tar.gz 368 | tar xzf lame-3.99.5.tar.gz 369 | cd lame-3.99.5 370 | ./configure 371 | make 372 | sudo make install 373 | ``` 374 | 375 | Build and install AAC encoder. 376 | ``` 377 | wget http://downloads.sourceforge.net/project/faac/faac-src/faac-1.28/faac-1.28.tar.gz 378 | tar xzf faac-1.28.tar.gz 379 | cd faac-1.28 380 | ./configure 381 | make 382 | sudo make install 383 | ``` 384 | 385 | Build and install the latest ffmpeg. At the time of this writing, the Raspbian version is too old and doesn't support H264. 386 | On PC: 387 | ``` 388 | scp -r programs/ffmpeg pi@rpi-cam: 389 | ``` 390 | On Pi: 391 | ``` 392 | cd ffmpeg 393 | ./configure --enable-shared --enable-gpl --prefix=/usr --enable-nonfree --enable-libmp3lame --enable-libfaac --enable-libx264 --enable-version3 --disable-mmx 394 | make 395 | sudo make install 396 | ``` 397 | 398 | ### Enable RTSP and RTMP streaming 399 | 400 | Install crtmpserver 401 | ``` 402 | sudo apt-get install crtmpserver 403 | ``` 404 | 405 | Edit the below section of /etc/crtmpserver/applications/flvplayback.lua 406 | ``` 407 | validateHandshake=false, 408 | keyframeSeek=false, 409 | seekGranularity=0.1 410 | clientSideBuffer=30 411 | ``` 412 | 413 | Restart crtmpserver 414 | ``` 415 | sudo service crtmpserver restart 416 | ``` 417 | 418 | Install nginx web server 419 | ``` 420 | sudo apt-get install nginx 421 | sudo server nginx start 422 | ``` 423 | 424 | Copy web server files 425 | On PC: 426 | ``` 427 | cd programs/crtmpserver 428 | unzip jwplayer-6.10.zip 429 | scp -r jwplayer pi@rpi-cam:/usr/share/nginx/www 430 | scp index.rtsp.html pi@rpi-cam:/usr/share/nginx/www/index.html 431 | ``` 432 | 433 | Restart nginx 434 | ``` 435 | sudo server nginx restart 436 | ``` 437 | 438 | Copy start up scripts. 439 | On PC: 440 | ``` 441 | scp programs/bbpicam pi@rpi-cam: 442 | scp programs/bbpicam_stream pi@rpi-cam: 443 | ``` 444 | On Pi: 445 | ``` 446 | sudo mkdir /opt/bbpicam 447 | sudo cp -f bbpicam_stream /opt/bbpicam/bbpicam_stream 448 | sudo cp -f bbpicam /etc/init.d/bbpicam 449 | sudo chown root:root /etc/init.d/bbpicam 450 | sudo service bbpicam start 451 | ``` 452 | 453 | Start audio video stream automatically at boot: 454 | ``` 455 | sudo insserv bbpicam 456 | ``` 457 | 458 | Point your browser to http://rpi-cam. I have tested this on Chrome and Firefox. 459 | If you want to play the stream from an Android device, try "RTSP Player" app. 460 | 461 | ### RGB Temperature LED 462 | 463 | The purpose of this RGB LED is to give a quick visual feedback of the room temperature. 464 | * Blue - lower than 18 degrees Celcius 465 | * Green - 22 degrees Celcius 466 | * Red - higher than 26 degrees Celcius 467 | 468 | Transfer required software to the Pi. 469 | On PC: 470 | ``` 471 | scp -r programs/hidapi pi@rpi-cam: 472 | scp -r programs/TEMPered pi@rpi-cam: 473 | scp programs/temper2led_init pi@rpi-cam: 474 | ``` 475 | 476 | On Pi, install the dependencies. 477 | ``` 478 | sudo apt-get install libudev-dev libusb-1.0-0-dev libusb-dev 479 | ``` 480 | 481 | add below line to /etc/modules 482 | ``` 483 | leds-pca9635 484 | ``` 485 | 486 | Build HIDAPI library. 487 | ``` 488 | cd hidapi 489 | ./bootstrap 490 | ./configure 491 | make 492 | cd - 493 | ``` 494 | 495 | Build TEMPered. 496 | ``` 497 | cd TEMPered 498 | cmake . 499 | make 500 | cd - 501 | ``` 502 | 503 | Find the TEMPer device 504 | ``` 505 | TEMPered/examples/enumerate 506 | ``` 507 | 508 | Install TEMPer 2 LED 509 | ``` 510 | sudo mkdir /opt/temper2led 511 | sudo cp -f TEMPered/examples/temper2led /opt/temper2led/temper2led 512 | sudo cp -f temper2led_init /etc/init.d/temper2led_init 513 | sudo chown root:root /etc/init.d/temper2led_init 514 | sudo service temper2led_init start 515 | ``` 516 | 517 | Start TEMPer 2 LED automatically at boot: 518 | ``` 519 | sudo insserv temper2led_init 520 | ``` 521 | 522 | ### Shutdown Button watcher 523 | 524 | Transfer shutdown_button to the Pi. 525 | On PC: 526 | ``` 527 | scp -r programs/shutdown_button pi@rpi-cam: 528 | ``` 529 | 530 | Build shutdown_button. 531 | ``` 532 | cd shutdown_button 533 | make 534 | cd - 535 | ``` 536 | 537 | Install shutdown_button. 538 | ``` 539 | sudo mkdir /opt/shutdown_button 540 | sudo cp -f shutdown_button/shutdown_button_init /etc/init.d/shutdown_button_init 541 | sudo cp -f shutdown_button/shutdown_button /opt/shutdown_button/shutdown_button 542 | sudo chown root:root /etc/init.d/shutdown_button_init 543 | sudo service shutdown_button_init start 544 | ``` 545 | 546 | Start shutdown_button automatically at boot: 547 | ``` 548 | sudo insserv shutdown_button_init 549 | ``` 550 | 551 | ### HLS (alternative) 552 | 553 | HLS is short for HTTP Live Streaming. HLS is implemented by Apple and only works well on Apple devices and Safari browser. 554 | 555 | HLS support on non-Apple devices or browsers: 556 | - Android : Broken on most devices. At best unreliable. 557 | - Firefox : Not supported. 558 | - Chrome : Not supported. 559 | - VLC : Works but does not automatically reload the playlist. 560 | 561 | HLS also has the disadvantage of high latency, which is unacceptable for a baby monitor. 562 | 563 | To switch to HLS protocol, try: 564 | ``` 565 | cd psips 566 | make 567 | make install 568 | mkfifo /tmp/live.h264 569 | raspivid -w 1280 -h 960 -fps 25 -t 0 -b 2400000 -o - | psips > /tmp/live.h264 & 570 | sudo LD_LIBRARY_PATH=/usr/local/lib ffmpeg -y -re -fflags +nobuffer -i /tmp/live.h264 -fflags +nobuffer -re -f alsa -ar 16000 -ac 2 -i hw:1,0 -map 0:0 -map 1:0 -c:v copy -strict -2 -c:a aac -b:a 32k -ac 1 -af "pan=1c|c0=c1" -f ssegment -segment_time 5 -segment_format mpegts -segment_list "/usr/share/nginx/www/live.m3u8" -segment_wrap 2 -segment_list_size 2 -segment_list_entry_prefix "live/" -segment_list_flags live -segment_list_type m3u8 "live/%08d.ts" 571 | ``` 572 | 573 | You also need to adjust /usr/share/nginx/www/index.html to serve the m3u8 playlist. 574 | 575 | ### Multicast (alternative) 576 | 577 | Be very careful when multicasting. If your switch does **not** support IGMP snooping, it **will** flood your network and may cause significant impact on your throughput. 578 | 579 | If you still want to try multicasting: 580 | ``` 581 | cd psips 582 | make 583 | make install 584 | mkfifo /tmp/live.h264 585 | raspivid -w 1280 -h 960 -fps 25 -t 0 -b 2400000 -o - | psips > live.h264 & 586 | LD_LIBRARY_PATH=/usr/local/lib ffmpeg -y -v debug -re -fflags +nobuffer -r 25.126 -i /home/pi/live.h264 -fflags +nobuffer -re -f alsa -ar 16000 -ac 2 -i hw:1,0 -map 0:0 -map 1:0 -c:v copy -strict -2 -c:a aac -b:a 32k -ac 1 -af "pan=1c|c0=c1" -f mpegts 'udp://239.255.255.100:1234?ttl=4&pkt_size=1400' 587 | ``` 588 | 589 | ### Motion (alternative for running security camera with motion detection) 590 | 591 | [Motion](http://www.lavrsen.dk/foswiki/bin/view/Motion/WebHome) is a program that monitors the video signal from cameras and detect motion. It includes a web server that serves the video feed as MJPEG and able to capture and save images when motion is detected. 592 | 593 | Install motion dependencies 594 | ``` 595 | sudo apt-get install libav-tools libavcodec54 libavdevice53 libavfilter2 libavfilter3 libavformat54 libavresample1 libavutil52 libdc1394-22 libmysqlclient18 libopencore-amrnb0 libopencore-amrwb0 libopencv-core2.3 libopencv-core2.4 libopencv-imgproc2.3 libopencv-imgproc2.4 libopus0 libpq5 libraw1394-11 libvo-aacenc0 libvo-amrwbenc0 libx264-130 mysql-common 596 | ``` 597 | 598 | Get pre-compiled motion binary and sample configuration file from programs/motion-mmal-opt.tar.gz 599 | 600 | Run motion and point your browser to http://rpi-cam:8081 601 | 602 | If you want to compile your own motion software, install devel libraries, git clone the Raspberry Pi motion-mmal repository: 603 | ``` 604 | sudo apt-get install libjpeg62 libjpeg62-dev libavformat53 libavformat-dev libavcodec53 libavcodec-dev libavutil51 libavutil-dev libc6-dev zlib1g-dev libmysqlclient18 libmysqlclient-dev libpq5 libpq-dev 605 | git clone https://github.com/dozencrows/motion.git motion-mmal 606 | cd motion-mmal 607 | git checkout mmal-test 608 | ``` 609 | Please refer to its BUILD-HOWTO file for build instructions. 610 | 611 | # Improvements? 612 | 613 | This is very much a prototype with breakout boards stuck together using cable ties and sticky tape. The enclosure is way too big as well. 614 | If I have more time, I certainly would like to improve a few things: 615 | * Add a speaker for playing lullabies and enable 2-way audio comms. 616 | * Bigger IR Light board. The current circular IR Light board is slightly too small and blocking the corners of the view. 617 | * Custom designed PCB that plugs onto the Pi's P1 and P5 headers, to replace all the breakout boards. 618 | * 3D printed enclosure. 619 | 620 | -------------------------------------------------------------------------------- /kernel/add-leds-pca9635.patch: -------------------------------------------------------------------------------- 1 | commit 2b246f71ee34d2db759420e044d7c066b7b336f9 2 | Author: Joo Aun Saw 3 | Date: Fri Sep 19 16:49:48 2014 +1000 4 | 5 | Add PCA9635 LEDs driver. 6 | 7 | diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig 8 | index 875bbe4..3dc803b 100644 9 | --- a/drivers/leds/Kconfig 10 | +++ b/drivers/leds/Kconfig 11 | @@ -300,6 +300,14 @@ config LEDS_PCA963X 12 | LED driver chip accessed via the I2C bus. Supported 13 | devices include PCA9633 and PCA9634 14 | 15 | +config LEDS_PCA9635 16 | + tristate "LED support for PCA9635 I2C chip" 17 | + depends on LEDS_CLASS 18 | + depends on I2C 19 | + help 20 | + This option enables support for LEDs connected to the PCA9635 21 | + LED driver chip accessed via the I2C bus. 22 | + 23 | config LEDS_WM831X_STATUS 24 | tristate "LED support for status LEDs on WM831x PMICs" 25 | depends on LEDS_CLASS 26 | diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile 27 | index 8979b0b..c657620 100644 28 | --- a/drivers/leds/Makefile 29 | +++ b/drivers/leds/Makefile 30 | @@ -36,6 +36,7 @@ obj-$(CONFIG_LEDS_OT200) += leds-ot200.o 31 | obj-$(CONFIG_LEDS_FSG) += leds-fsg.o 32 | obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o 33 | obj-$(CONFIG_LEDS_PCA963X) += leds-pca963x.o 34 | +obj-$(CONFIG_LEDS_PCA9635) += leds-pca9635.o 35 | obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o 36 | obj-$(CONFIG_LEDS_DA9052) += leds-da9052.o 37 | obj-$(CONFIG_LEDS_WM831X_STATUS) += leds-wm831x-status.o 38 | diff --git a/drivers/leds/leds-pca9635.c b/drivers/leds/leds-pca9635.c 39 | new file mode 100644 40 | index 0000000..a29fcd0 41 | --- /dev/null 42 | +++ b/drivers/leds/leds-pca9635.c 43 | @@ -0,0 +1,710 @@ 44 | +/* 45 | + * pca9635.c - NXP LED driver 46 | + * 47 | + * Copyright (C) 2010 Joo Aun Saw 48 | + * Original author Doug Bailey 49 | + * 50 | + * This program is free software; you can redistribute it and/or 51 | + * modify it under the terms of the GNU General Public License 52 | + * as published by the Free Software Foundation; either version 2 53 | + * of the License, or (at your option) any later version. 54 | + * 55 | + * This program is distributed in the hope that it will be useful, 56 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of 57 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 58 | + * GNU General Public License for more details. 59 | + * 60 | + * You should have received a copy of the GNU General Public License 61 | + * along with this program; if not, write to the Free Software 62 | + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 63 | + * USA. 64 | +*/ 65 | + 66 | +//#include 67 | +//#include 68 | +#include 69 | +#include 70 | +#include 71 | +#include 72 | +//#include 73 | +//#include 74 | +#include 75 | +//#include 76 | +//#include 77 | +//#include 78 | +//#include 79 | +#include 80 | + 81 | +#define NUM_LEDS 16 82 | +#define BRIGHTNESS_MASK 0xff 83 | + 84 | +//#define PCA9635_PROC_FNAME "pca9635" 85 | + 86 | +#define PCA9635_AUTOINC_MASK_NONE (0x00) 87 | +#define PCA9635_AUTOINC_MASK_ALL (0x80) 88 | +#define PCA9635_AUTOINC_MASK_BRIGHT (0xa0) 89 | +#define PCA9635_AUTOINC_MASK_GLBCTL (0xc0) 90 | +#define PCA9635_AUTOINC_MASK_ALLCTL (0xe0) 91 | + 92 | +#define PCA9635_REG_MODE1 0x0 93 | +#define PCA9635_REG_MODE2 0x1 94 | +#define PCA9635_REG_BRIGHTNESS0 0x2 95 | +#define PCA9635_REG_GRPPWM 0x12 96 | +#define PCA9635_REG_GRPFREQ 0x13 97 | +#define PCA9635_REG_LEDOUT0 0x14 98 | +#define PCA9635_REG_SUBADR1 0x18 99 | +#define PCA9635_REG_SUBADR2 0x19 100 | +#define PCA9635_REG_SUBADR3 0x1a 101 | +#define PCA9635_REG_ALLCALLADR 0x1b 102 | +#define PCA9635_NUM_REGS (PCA9635_REG_ALLCALLADR +1) 103 | +#define PCA9635_REG_DUMMY 0x1f 104 | + 105 | +#define PCA9635_REG_MODE1_SLEEP_MASK 0x10 106 | +#define PCA9635_REG_MODE2_GRP_BLINK_MASK 0x20 107 | + 108 | + 109 | + 110 | +struct pca9635_led_data 111 | +{ 112 | + struct i2c_client *client; 113 | +// struct mutex update_lock; 114 | + unsigned char ind_config_mode_1; 115 | + unsigned char ind_config_mode_2; 116 | + unsigned char ind_group_brightness; 117 | + unsigned char ind_group_frequency; 118 | + unsigned char ind_led_state[NUM_LEDS]; 119 | + unsigned char ind_led_brightness[NUM_LEDS]; 120 | + // Work queue to allow for background update of led state and brightness registers 121 | + struct delayed_work update_state_wq; 122 | + struct delayed_work update_brightness_wq; 123 | +}; 124 | + 125 | + 126 | + 127 | + 128 | +#define isdigit(c) (c >= '0' && c <= '9') 129 | +#define isxdigit(c) ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) 130 | +#define tonumber(x) ((x)-'0') 131 | +static unsigned char tohex(unsigned char c) 132 | +{ 133 | + unsigned char digit; 134 | + if (isdigit(c)) 135 | + digit = c - '0'; 136 | + else 137 | + digit = (c | 0x20) - 'a' + 10; 138 | + return digit; 139 | +} 140 | + 141 | + 142 | + 143 | +static ssize_t pca9635_get_led_state(struct device *dev, struct device_attribute *attr, char *buf) 144 | +{ 145 | + struct i2c_client *client = to_i2c_client(dev); 146 | + struct pca9635_led_data *data = i2c_get_clientdata(client); 147 | + int ret = 0; 148 | + int i; 149 | + 150 | + for (i = 0; i < NUM_LEDS; i++) 151 | + ret += sprintf(buf+ret, "%X: %X\n", i, data->ind_led_state[i]); 152 | + 153 | + return ret; 154 | +} 155 | +static ssize_t pca9635_set_led_state(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 156 | +{ 157 | + struct i2c_client *client = to_i2c_client(dev); 158 | + struct pca9635_led_data *data = i2c_get_clientdata(client); 159 | + unsigned char new_led_state[NUM_LEDS]; 160 | + int i; 161 | + int led_index; 162 | + 163 | + memcpy(new_led_state, data->ind_led_state, NUM_LEDS); 164 | + for (i = 0; i < count; i = i + 2) 165 | + { 166 | + if ((i + 1) < count) 167 | + { 168 | + if ((isxdigit(buf[i])) && (isdigit(buf[i+1]))) 169 | + { 170 | + led_index = tohex(buf[i]); 171 | + if (led_index < NUM_LEDS) 172 | + { 173 | + new_led_state[led_index] = tonumber(buf[i+1]) & 0x3; 174 | + } 175 | + } 176 | + else 177 | + { 178 | + return -EINVAL; 179 | + } 180 | + } 181 | + else 182 | + { 183 | + break; 184 | + } 185 | + } 186 | + 187 | + if (memcmp(new_led_state, data->ind_led_state, NUM_LEDS) != 0) 188 | + { 189 | + memcpy(data->ind_led_state, new_led_state, NUM_LEDS); 190 | + schedule_delayed_work(&data->update_state_wq, 0); 191 | + } 192 | + 193 | + return count; 194 | +} 195 | +static DEVICE_ATTR(led_state, S_IWUSR | S_IRUGO, pca9635_get_led_state, pca9635_set_led_state); 196 | + 197 | + 198 | +static ssize_t pca9635_get_led_brightness(struct device *dev, struct device_attribute *attr, char *buf) 199 | +{ 200 | + struct i2c_client *client = to_i2c_client(dev); 201 | + struct pca9635_led_data *data = i2c_get_clientdata(client); 202 | + int ret = 0; 203 | + int i; 204 | + 205 | + for (i = 0; i < NUM_LEDS; i++) 206 | + ret += sprintf(buf+ret, "%X: 0x%02X\n", i, data->ind_led_brightness[i]); 207 | + 208 | + return ret; 209 | +} 210 | +static ssize_t pca9635_set_led_brightness(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 211 | +{ 212 | + struct i2c_client *client = to_i2c_client(dev); 213 | + struct pca9635_led_data *data = i2c_get_clientdata(client); 214 | + unsigned char new_led_brightness[NUM_LEDS]; 215 | + int i; 216 | + int led_index; 217 | + 218 | + memcpy(new_led_brightness, data->ind_led_brightness, NUM_LEDS); 219 | + for (i = 0; i < count; i = i + 3) 220 | + { 221 | + if ((i + 2) < count) 222 | + { 223 | + if ((isxdigit(buf[i])) && (isxdigit(buf[i+1])) && (isxdigit(buf[i+2]))) 224 | + { 225 | + led_index = tohex(buf[i]); 226 | + if (led_index < NUM_LEDS) 227 | + { 228 | + new_led_brightness[led_index] = (tohex(buf[i+1]) * 16) + tohex(buf[i+2]); 229 | + } 230 | + } 231 | + else 232 | + { 233 | + return -EINVAL; 234 | + } 235 | + } 236 | + else 237 | + { 238 | + break; 239 | + } 240 | + } 241 | + 242 | + if (memcmp(new_led_brightness, data->ind_led_brightness, NUM_LEDS) != 0) 243 | + { 244 | + memcpy(data->ind_led_brightness, new_led_brightness, NUM_LEDS); 245 | + schedule_delayed_work(&data->update_brightness_wq, 0); 246 | + } 247 | + 248 | + return count; 249 | +} 250 | +static DEVICE_ATTR(led_brightness, S_IWUSR | S_IRUGO, pca9635_get_led_brightness, pca9635_set_led_brightness); 251 | + 252 | + 253 | +static ssize_t pca9635_get_group_blink_freq(struct device *dev, struct device_attribute *attr, char *buf) 254 | +{ 255 | + struct i2c_client *client = to_i2c_client(dev); 256 | + struct pca9635_led_data *data = i2c_get_clientdata(client); 257 | + int ret; 258 | + 259 | + ret = sprintf(buf, "0x%02X\n", data->ind_group_frequency); 260 | + 261 | + return ret; 262 | +} 263 | +static ssize_t pca9635_set_group_blink_freq(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 264 | +{ 265 | + struct i2c_client *client = to_i2c_client(dev); 266 | + struct pca9635_led_data *data = i2c_get_clientdata(client); 267 | + int bytes = 0; 268 | + u8 msg[2]; 269 | + int result = count; 270 | + 271 | + if (count > 1) 272 | + { 273 | + if ((isxdigit(buf[0])) && (isxdigit(buf[1]))) 274 | + { 275 | + data->ind_group_frequency = (tohex(buf[0]) * 16) + tohex(buf[1]); 276 | + // write new value 277 | + msg[0] = PCA9635_REG_GRPFREQ; 278 | + msg[1] = data->ind_group_frequency; 279 | + bytes = i2c_master_send(client, msg, 2); 280 | + if (bytes != 2) 281 | + { 282 | + dev_warn(&client->dev, "Failed to set Group Blink Frequency\n"); 283 | + result = -EIO; 284 | + } 285 | + } 286 | + else 287 | + { 288 | + result = -EINVAL; 289 | + } 290 | + } 291 | + 292 | + return result; 293 | +} 294 | +static DEVICE_ATTR(group_blink_freq, S_IWUSR | S_IRUGO, pca9635_get_group_blink_freq, pca9635_set_group_blink_freq); 295 | + 296 | + 297 | +static ssize_t pca9635_get_group_brightness_blink(struct device *dev, struct device_attribute *attr, char *buf) 298 | +{ 299 | + struct i2c_client *client = to_i2c_client(dev); 300 | + struct pca9635_led_data *data = i2c_get_clientdata(client); 301 | + int ret; 302 | + 303 | + ret = sprintf(buf, "0x%02X\n", data->ind_group_brightness); 304 | + 305 | + return ret; 306 | +} 307 | +static ssize_t pca9635_set_group_brightness_blink(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 308 | +{ 309 | + struct i2c_client *client = to_i2c_client(dev); 310 | + struct pca9635_led_data *data = i2c_get_clientdata(client); 311 | + int bytes = 0; 312 | + u8 msg[2]; 313 | + int result = count; 314 | + 315 | + if (count > 0) 316 | + { 317 | + if ((isxdigit(buf[0])) && (isxdigit(buf[1]))) 318 | + { 319 | + data->ind_group_brightness = (tohex(buf[0]) * 16) + tohex(buf[1]); 320 | + // write new value 321 | + msg[0] = PCA9635_REG_GRPPWM; 322 | + msg[1] = data->ind_group_brightness; 323 | + bytes = i2c_master_send(client, msg, 2); 324 | + if (bytes != 2) 325 | + { 326 | + dev_warn(&client->dev, "Failed to set Group Brightness\n"); 327 | + result = -EIO; 328 | + } 329 | + } 330 | + else 331 | + { 332 | + result = -EINVAL; 333 | + } 334 | + } 335 | + 336 | + return result; 337 | +} 338 | +static DEVICE_ATTR(group_brightness_blink, S_IWUSR | S_IRUGO, pca9635_get_group_brightness_blink, pca9635_set_group_brightness_blink); 339 | + 340 | + 341 | +static ssize_t pca9635_get_group_blink_enable(struct device *dev, struct device_attribute *attr, char *buf) 342 | +{ 343 | + struct i2c_client *client = to_i2c_client(dev); 344 | + struct pca9635_led_data *data = i2c_get_clientdata(client); 345 | + int ret; 346 | + 347 | + ret = sprintf(buf, "%c\n", (data->ind_config_mode_2 & PCA9635_REG_MODE2_GRP_BLINK_MASK) ? '1' : '0'); 348 | + 349 | + return ret; 350 | +} 351 | +static ssize_t pca9635_set_group_blink_enable(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 352 | +{ 353 | + struct i2c_client *client = to_i2c_client(dev); 354 | + struct pca9635_led_data *data = i2c_get_clientdata(client); 355 | + int bytes = 0; 356 | + u8 msg[2]; 357 | + int result = count; 358 | + 359 | + if (count > 0) 360 | + { 361 | + switch (buf[0]) 362 | + { 363 | + case '1': 364 | + data->ind_config_mode_2 |= PCA9635_REG_MODE2_GRP_BLINK_MASK; 365 | + break; 366 | + case '0': 367 | + data->ind_config_mode_2 &= ~PCA9635_REG_MODE2_GRP_BLINK_MASK; 368 | + break; 369 | + default: 370 | + result = -EINVAL; 371 | + } 372 | + if (result > 0) 373 | + { 374 | + // write new value 375 | + msg[0] = PCA9635_REG_MODE2; 376 | + msg[1] = data->ind_config_mode_2; 377 | + bytes = i2c_master_send(client, msg, 2); 378 | + if (bytes != 2) 379 | + { 380 | + dev_warn(&client->dev, "Failed to set Group Blink mode\n"); 381 | + result = -EIO; 382 | + } 383 | + } 384 | + } 385 | + 386 | + return result; 387 | +} 388 | +static DEVICE_ATTR(group_blink_enable, S_IWUSR | S_IRUGO, pca9635_get_group_blink_enable, pca9635_set_group_blink_enable); 389 | + 390 | + 391 | +static ssize_t pca9635_get_sleep(struct device *dev, struct device_attribute *attr, char *buf) 392 | +{ 393 | + struct i2c_client *client = to_i2c_client(dev); 394 | + struct pca9635_led_data *data = i2c_get_clientdata(client); 395 | + int ret; 396 | + 397 | + ret = sprintf(buf, "%c\n", (data->ind_config_mode_1 & PCA9635_REG_MODE1_SLEEP_MASK) ? '1' : '0'); 398 | + 399 | + return ret; 400 | +} 401 | +static ssize_t pca9635_set_sleep(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 402 | +{ 403 | + struct i2c_client *client = to_i2c_client(dev); 404 | + struct pca9635_led_data *data = i2c_get_clientdata(client); 405 | + int bytes = 0; 406 | + u8 msg[2]; 407 | + int result = count; 408 | + 409 | + if (count > 0) 410 | + { 411 | + switch (buf[0]) 412 | + { 413 | + case '1': 414 | + data->ind_config_mode_1 |= PCA9635_REG_MODE1_SLEEP_MASK; 415 | + break; 416 | + case '0': 417 | + data->ind_config_mode_1 &= ~PCA9635_REG_MODE1_SLEEP_MASK; 418 | + break; 419 | + default: 420 | + result = -EINVAL; 421 | + } 422 | + if (result > 0) 423 | + { 424 | + // write new value 425 | + msg[0] = PCA9635_REG_MODE1; 426 | + msg[1] = data->ind_config_mode_1; 427 | + bytes = i2c_master_send(client, msg, 2); 428 | + if (bytes != 2) 429 | + { 430 | + dev_warn(&client->dev, "Failed to set Sleep mode\n"); 431 | + result = -EIO; 432 | + } 433 | + } 434 | + } 435 | + 436 | + return result; 437 | +} 438 | +static DEVICE_ATTR(sleep, S_IWUSR | S_IRUGO, pca9635_get_sleep, pca9635_set_sleep); 439 | + 440 | + 441 | + 442 | + 443 | + 444 | +static struct attribute *pca9635_attributes[] = { 445 | + &dev_attr_sleep.attr, 446 | + &dev_attr_group_blink_enable.attr, 447 | + &dev_attr_group_brightness_blink.attr, 448 | + &dev_attr_group_blink_freq.attr, 449 | + &dev_attr_led_brightness.attr, 450 | + &dev_attr_led_state.attr, 451 | + NULL 452 | +}; 453 | + 454 | + 455 | +static const struct attribute_group pca9635_attr_group = { 456 | + .attrs = pca9635_attributes, 457 | +}; 458 | + 459 | + 460 | + 461 | + 462 | + 463 | + 464 | +/* Sends i2c stream that sets the state of the LEDs on the driver 465 | + * 466 | + * Use i2c_transfer due to unique capability to set led driver for auto increment 467 | + * (SMB functions did not allow for multiple byte transactions due to 468 | + * length byte in data payload.) 469 | +*/ 470 | +static void refresh_led_state(struct work_struct *work) 471 | +{ 472 | + struct delayed_work *tmp_delayed_work = container_of(work, struct delayed_work, work); 473 | + struct pca9635_led_data *data = container_of(tmp_delayed_work, struct pca9635_led_data, update_state_wq); 474 | + struct i2c_client *client = data->client; 475 | + 476 | + int i; 477 | + int res; 478 | + u8 led_onoff[NUM_LEDS/4+1]; 479 | + 480 | + struct i2c_msg msg = 481 | + { .addr = 0, 482 | + .flags = 0, // Write this data 483 | + .len = NUM_LEDS/4+1, 484 | + .buf = led_onoff 485 | + }; 486 | + 487 | + if (data != NULL) 488 | + { 489 | + msg.addr = client->addr; 490 | + led_onoff[0] = PCA9635_REG_LEDOUT0 + PCA9635_AUTOINC_MASK_ALL; 491 | + for (i = 1; i <= NUM_LEDS/4; i++) 492 | + led_onoff[i]= 0; 493 | + for (i = 0; i < NUM_LEDS; i++) 494 | + led_onoff[i/4+1] |= ( data->ind_led_state[i] & 0x3) << (2*(i % 4)); 495 | + 496 | + res = i2c_transfer(client->adapter, &msg, 1); 497 | + if (res < 0) 498 | + { 499 | + schedule_delayed_work(&data->update_state_wq, msecs_to_jiffies(100)); 500 | + dev_warn(&client->dev, "Failed to refresh LED state\n"); 501 | + } 502 | + } 503 | + //dev_info(&client->dev, "LED state refreshed\n"); 504 | +} 505 | + 506 | + 507 | +// Send i2c stream that sets the brightness registers in the IC 508 | +static void refresh_led_brightness(struct work_struct *work) 509 | +{ 510 | + struct delayed_work *tmp_delayed_work = container_of(work, struct delayed_work, work); 511 | + struct pca9635_led_data *data = container_of(tmp_delayed_work, struct pca9635_led_data, update_brightness_wq); 512 | + struct i2c_client *client = data->client; 513 | + 514 | + int i; 515 | + int res; 516 | + u8 led_brightness[NUM_LEDS+1]; 517 | + 518 | + struct i2c_msg msg = 519 | + { .addr = 0, 520 | + .flags = 0, // Write this data 521 | + .len = NUM_LEDS+1, 522 | + .buf = led_brightness 523 | + }; 524 | + 525 | + if (data != NULL) 526 | + { 527 | + msg.addr = client->addr; 528 | + led_brightness[0] = PCA9635_REG_BRIGHTNESS0 + PCA9635_AUTOINC_MASK_ALL; 529 | + for (i = 0; i < NUM_LEDS; i++) 530 | + led_brightness[i+1] = data->ind_led_brightness[i]; 531 | + res = i2c_transfer(client->adapter, &msg, 1); 532 | + if (res < 0) 533 | + { 534 | + schedule_delayed_work(&data->update_brightness_wq, msecs_to_jiffies(100)); 535 | + dev_warn(&client->dev, "Failed to refresh LED brightness\n"); 536 | + } 537 | + } 538 | + //dev_info(&client->dev, "LED brightness refreshed\n"); 539 | +} 540 | + 541 | + 542 | +static int pca9635_configure(struct i2c_client *client) 543 | +{ 544 | + struct pca9635_led_data *data = i2c_get_clientdata(client); 545 | + int i; 546 | + int res; 547 | + u8 init_data[PCA9635_NUM_REGS +1]; 548 | + struct i2c_msg msg = 549 | + { 550 | + .addr = 0, 551 | + .flags = 0, // Write this data 552 | +// .len = PCA9635_NUM_REGS + 1 -4, // Not setting the ALL CALL rtegs 553 | + .len = PCA9635_NUM_REGS + 1, 554 | + .buf = init_data, 555 | + }; 556 | + 557 | + msg.addr = client->addr; 558 | + // specifiy the first register address and set auto- increment 559 | + init_data[0] = PCA9635_REG_MODE1 | PCA9635_AUTOINC_MASK_ALL; 560 | + 561 | + init_data[PCA9635_REG_MODE1+1] = data->ind_config_mode_1; 562 | + init_data[PCA9635_REG_MODE2+1] = data->ind_config_mode_2; 563 | + 564 | + for (i = 0; i < NUM_LEDS; i++) 565 | + init_data[PCA9635_REG_BRIGHTNESS0 + i +1] = data->ind_led_brightness[i]; 566 | + 567 | + init_data[PCA9635_REG_GRPPWM +1] = data->ind_group_brightness; 568 | + init_data[PCA9635_REG_GRPFREQ +1] = data->ind_group_frequency; 569 | + 570 | + for (i = 0; i < NUM_LEDS/4; i++) 571 | + init_data[PCA9635_REG_LEDOUT0 + i +1] = 0; 572 | + for (i = 0; i < NUM_LEDS; i++) 573 | + init_data[PCA9635_REG_LEDOUT0 + (i/4) +1] |= (data->ind_led_state[i] & 0x3) << (2*(i % 4)); 574 | + 575 | +//#if 0 // Going to leave to defaults for now 576 | + init_data[PCA9635_REG_SUBADR1 +1] = 0; 577 | + init_data[PCA9635_REG_SUBADR2 +1] = 0; 578 | + init_data[PCA9635_REG_SUBADR3 +1] = 0; 579 | + init_data[PCA9635_REG_ALLCALLADR +1] = 0; 580 | +//#endif 581 | + 582 | + res = i2c_transfer(client->adapter, &msg, 1); 583 | + 584 | + return res; 585 | +} 586 | + 587 | + 588 | +static void pca9635_reset_i2c_interface(struct i2c_client *client) 589 | +{ 590 | + //struct pca9635_led_data *data = i2c_get_clientdata(client); 591 | + u8 dummy_data; 592 | + struct i2c_msg msg = 593 | + { 594 | + .addr = 0, 595 | + .flags = 0, // Write this data 596 | + .len = 1, 597 | + .buf = &dummy_data, 598 | + }; 599 | + 600 | + msg.addr = client->addr; 601 | + dummy_data = PCA9635_REG_DUMMY; 602 | + i2c_transfer(client->adapter, &msg, 1); 603 | +} 604 | +//static int pca9635_reset_i2c_interface(struct i2c_client *client) 605 | +//{ 606 | +// //struct pca9635_led_data *data = i2c_get_clientdata(client); 607 | +// int res; 608 | +// u8 dummy_data[2]; 609 | +// struct i2c_msg msg = 610 | +// { 611 | +// .addr = 0, 612 | +// .flags = 1, // read this data 613 | +// .len = 2, 614 | +// .buf = dummy_data, 615 | +// }; 616 | +// 617 | +// msg.addr = client->addr; 618 | +// dummy_data[0] = PCA9635_REG_MODE1; 619 | +// res = i2c_transfer(client->adapter, &msg, 1); 620 | +// 621 | +// return res; 622 | +//} 623 | + 624 | + 625 | +static int pca9635_probe(struct i2c_client *client, const struct i2c_device_id *id) 626 | +{ 627 | + struct pca9635_led_data *data; 628 | + int err; 629 | + int i; 630 | + 631 | + dev_info(&client->dev, "Probe\n"); 632 | + 633 | + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) 634 | + return -EIO; 635 | + 636 | + if (!(data = kzalloc(sizeof(struct pca9635_led_data), GFP_KERNEL))) 637 | + { 638 | + err = -ENOMEM; 639 | + goto exit; 640 | + } 641 | + 642 | + i2c_set_clientdata(client, data); 643 | + data->client = client; 644 | +// mutex_init(&data->update_lock); 645 | + INIT_DELAYED_WORK(&data->update_state_wq, refresh_led_state); 646 | + INIT_DELAYED_WORK(&data->update_brightness_wq, refresh_led_brightness); 647 | + 648 | + for (i = 0; i < NUM_LEDS; i++) 649 | + data->ind_led_brightness[i] = 0x00; // minimum brightness 650 | + for (i = 0; i < NUM_LEDS; i++) 651 | + data->ind_led_state[i] = 0x00; // All led's off 652 | + data->ind_config_mode_1 = 0x00; // Power/Osc on, no all calls 653 | + data->ind_config_mode_2 = 0x01; // no group blink, no invert output, update on STOP, open-drain 654 | + data->ind_group_brightness = 0xFF; // full brightness 655 | + data->ind_group_frequency = 23; // 1 second period 656 | + 657 | + // Initialize the PCA9635 chip 658 | + pca9635_reset_i2c_interface(client); 659 | +// for (i = 0; i < 5; i++) 660 | +// { 661 | +// err = pca9635_reset_i2c_interface(client); 662 | +// if (err < 0) 663 | +// { 664 | +// dev_warn(&client->dev, "Read device attempt %d failed\n", i+1); 665 | +// mdelay(100); 666 | +// } 667 | +// else 668 | +// break; 669 | +// } 670 | +// dev_info(&client->dev, "Configure\n"); 671 | + for (i = 0; i < 3; i++) 672 | + { 673 | + err = pca9635_configure(client); 674 | + if (err < 0) 675 | + { 676 | +// dev_warn(&client->dev, "Configure device attempt %d failed\n", i+1); 677 | + mdelay(20); 678 | + } 679 | + else 680 | + break; 681 | + } 682 | + if (err < 0) 683 | + { 684 | + dev_err(&client->dev, "Failed to configure device\n"); 685 | + goto exit_kfree; 686 | + } 687 | + 688 | + // DEBUG !!! 689 | +// schedule_work(&data->update_state_wq); 690 | +// schedule_work(&data->update_brightness_wq); 691 | + 692 | + // Register sysfs hooks 693 | + err = sysfs_create_group(&client->dev.kobj, &pca9635_attr_group); 694 | + if (err) 695 | + { 696 | + dev_err(&client->dev, "Failed to sysfs interface\n"); 697 | + goto exit_kfree; 698 | + } 699 | + 700 | + dev_info(&client->dev, "Initialized\n"); 701 | + 702 | + return 0; 703 | + 704 | +exit_kfree: 705 | + kfree(data); 706 | + i2c_set_clientdata(client, NULL); 707 | +exit: 708 | + return err; 709 | +} 710 | + 711 | + 712 | +static int pca9635_remove(struct i2c_client *client) 713 | +{ 714 | + struct pca9635_led_data *data = i2c_get_clientdata(client); 715 | + 716 | + sysfs_remove_group(&client->dev.kobj, &pca9635_attr_group); 717 | + 718 | + cancel_delayed_work(&data->update_state_wq); 719 | + cancel_delayed_work(&data->update_brightness_wq); 720 | + flush_delayed_work(&data->update_state_wq); 721 | + flush_delayed_work(&data->update_brightness_wq); 722 | +// flush_scheduled_work(); 723 | + 724 | + kfree(data); 725 | + i2c_set_clientdata(client, NULL); 726 | + 727 | + return 0; 728 | +} 729 | + 730 | + 731 | +static const struct i2c_device_id pca9635_id[] = { 732 | + { "pca9635", 0 }, 733 | + { } 734 | +}; 735 | +MODULE_DEVICE_TABLE(i2c, pca9635_id); 736 | + 737 | + 738 | +static struct i2c_driver pca9635_driver = { 739 | + .driver = { 740 | + .name = "pca9635", 741 | + .owner = THIS_MODULE, 742 | + }, 743 | + .probe = pca9635_probe, 744 | + .remove = pca9635_remove, 745 | + .id_table = pca9635_id, 746 | +}; 747 | + 748 | +module_i2c_driver(pca9635_driver); 749 | + 750 | + 751 | +MODULE_AUTHOR("Joo Aun Saw"); 752 | +MODULE_DESCRIPTION("I2C PCA9635 LED driver"); 753 | +MODULE_LICENSE("GPL"); 754 | --------------------------------------------------------------------------------