From 0fe7b8becf26816eb303626addc8d34821e361f3 Mon Sep 17 00:00:00 2001 From: Amaury Pouly Date: Fri, 3 Nov 2017 20:14:01 +0100 Subject: [PATCH] nwzlinux: add support for radio None of the Sony up to A15 seem to support RDS (they use either Si4702 or Si4708), thus I did not add any code to support RDS. Change-Id: I64838993b9705b36b94665f8470c7a89c772c961 --- apps/audio_path.c | 3 +- apps/debug_menu.c | 8 +- firmware/SOURCES | 4 +- firmware/drivers/audio/nwzlinux-codec.c | 15 ++- firmware/export/config/sonynwzlinux.h | 4 + firmware/export/nwzlinux_codec.h | 9 ++ firmware/target/hosted/sonynwz/audio-nwz.c | 65 ++++++++++++ firmware/target/hosted/sonynwz/nwz_tuner.h | 61 ++++++++++++ firmware/target/hosted/sonynwz/radio-nwz.c | 110 +++++++++++++++++++++ tools/configure | 18 ++-- 10 files changed, 279 insertions(+), 18 deletions(-) create mode 100644 firmware/target/hosted/sonynwz/audio-nwz.c create mode 100644 firmware/target/hosted/sonynwz/nwz_tuner.h create mode 100644 firmware/target/hosted/sonynwz/radio-nwz.c diff --git a/apps/audio_path.c b/apps/audio_path.c index 3f43badc25..bb73052a95 100644 --- a/apps/audio_path.c +++ b/apps/audio_path.c @@ -45,7 +45,8 @@ #endif #endif -#if ((CONFIG_PLATFORM & PLATFORM_NATIVE) || defined(SAMSUNG_YPR0) || defined(SAMSUNG_YPR1)) +#if ((CONFIG_PLATFORM & PLATFORM_NATIVE) || defined(SAMSUNG_YPR0) || defined(SAMSUNG_YPR1) \ + || defined(SONY_NWZ_LINUX)) #ifdef AUDIO_CPU_BOOST static void audio_cpu_boost(bool state) diff --git a/apps/debug_menu.c b/apps/debug_menu.c index 0a1ceee2e0..1cccdfbcf4 100644 --- a/apps/debug_menu.c +++ b/apps/debug_menu.c @@ -71,14 +71,12 @@ #if (CONFIG_STORAGE & STORAGE_ATA) #include "ata.h" #endif -#if CONFIG_TUNER -#include "tuner.h" -#include "radio.h" -#endif #endif /* CONFIG_PLATFORM & PLATFORM_NATIVE */ #include "power.h" -#if (defined(SAMSUNG_YPR0) || defined(SAMSUNG_YPR1)) && defined(CONFIG_TUNER) +#if ((CONFIG_PLATFORM & PLATFORM_NATIVE) || defined(SAMSUNG_YPR0) || defined(SAMSUNG_YPR1) \ + || defined(SONY_NWZ_LINUX)) \ + && defined(CONFIG_TUNER) #include "tuner.h" #include "radio.h" #endif diff --git a/firmware/SOURCES b/firmware/SOURCES index c29b5318d5..9337c14234 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -106,6 +106,8 @@ target/hosted/sonynwz/system-nwz.c target/hosted/sonynwz/powermgmt-nwz.c target/hosted/sonynwz/power-nwz.c target/hosted/sonynwz/adc-nwz.c +target/hosted/sonynwz/radio-nwz.c +target/hosted/sonynwz/audio-nwz.c target/hosted/sonynwz/debug-nwz.c target/hosted/sonynwz/nvp-nwz.c target/hosted/sonynwz/nwz-db.c @@ -361,7 +363,7 @@ drivers/rtc/rtc_imx233.c /* Tuner */ #if CONFIG_TUNER tuner.c -#if ((CONFIG_PLATFORM & PLATFORM_NATIVE) || defined(SAMSUNG_YPR0) || defined(SAMSUNG_YPR1)) +#if ((CONFIG_PLATFORM & PLATFORM_NATIVE) || defined(SAMSUNG_YPR0) || defined(SAMSUNG_YPR1) || defined(SONY_NWZ_LINUX)) #if (CONFIG_TUNER & LV24020LP) drivers/tuner/lv24020lp.c #endif /* (CONFIG_TUNER & LV24020LP) */ diff --git a/firmware/drivers/audio/nwzlinux-codec.c b/firmware/drivers/audio/nwzlinux-codec.c index 7d730ce68b..33059d2aec 100644 --- a/firmware/drivers/audio/nwzlinux-codec.c +++ b/firmware/drivers/audio/nwzlinux-codec.c @@ -300,6 +300,17 @@ void audiohw_enable_cuerev(bool en) alsa_controls_set_bool("CODEC Cue/Rev Switch", en); } +void audiohw_set_playback_src(enum nwz_src_t src) +{ + switch(src) + { + case NWZ_RADIO: alsa_controls_set_enum("Playback Src Switch", "Fm"); break; + case NWZ_MIC: alsa_controls_set_enum("Playback Src Switch", "Mic"); break; + case NWZ_PLAYBACK: + default: alsa_controls_set_enum("Playback Src Switch", "Music"); break; + } +} + void audiohw_preinit(void) { alsa_controls_init(); @@ -312,8 +323,8 @@ void audiohw_preinit(void) * them by default */ audiohw_enable_acoustic(false); audiohw_enable_cuerev(false); - /* not sure exactly what it means */ - alsa_controls_set_enum("Playback Src Switch", "Music"); + /* select playback source */ + audiohw_set_playback_src(NWZ_PLAYBACK); /* use headphone output */ alsa_controls_set_enum("Output Switch", "Headphone"); /* unmute */ diff --git a/firmware/export/config/sonynwzlinux.h b/firmware/export/config/sonynwzlinux.h index 3ca0e014cb..00b606b1e3 100644 --- a/firmware/export/config/sonynwzlinux.h +++ b/firmware/export/config/sonynwzlinux.h @@ -62,9 +62,13 @@ /* Define this if you do software codec */ #define CONFIG_CODEC SWCODEC +#define CONFIG_TUNER SI4700 + /* There is no hardware tone control */ #define HAVE_SW_TONE_CONTROLS +#define INPUT_SRC_CAPS SRC_CAP_FMRADIO + /* The A15 and A25 support more sampling rates, in fact they support crazy high bit-rates such * as 176.4 and 192 kHz but Rockbox does not support those */ #if defined(SONY_NWZA10) || defined(SONY_NWA20) diff --git a/firmware/export/nwzlinux_codec.h b/firmware/export/nwzlinux_codec.h index dae8cca76f..06557ea1ca 100644 --- a/firmware/export/nwzlinux_codec.h +++ b/firmware/export/nwzlinux_codec.h @@ -26,11 +26,20 @@ /* Ranges from -100dB to 4dB */ AUDIOHW_SETTING(VOLUME, "dB", 0, 1, -100, 4, -10) +enum nwz_src_t +{ + NWZ_PLAYBACK, + NWZ_RADIO, + NWZ_MIC, +}; + /* enable/disable Sony's "acoustic" mode */ bool audiohw_acoustic_enabled(void); void audiohw_enable_acoustic(bool en); /* enable/disable Sony's "cuerev" mode */ bool audiohw_cuerev_enabled(void); void audiohw_enable_cuerev(bool en); +/* select playback source */ +void audiohw_set_playback_src(enum nwz_src_t src); #endif /* __NWZLINUX_CODEC_H__ */ diff --git a/firmware/target/hosted/sonynwz/audio-nwz.c b/firmware/target/hosted/sonynwz/audio-nwz.c new file mode 100644 index 0000000000..d119ee736d --- /dev/null +++ b/firmware/target/hosted/sonynwz/audio-nwz.c @@ -0,0 +1,65 @@ +/*************************************************************************** + * __________ __ ___ + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2017 Amaury Pouly + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "config.h" +#include "system.h" +#include "cpu.h" +#include "audio.h" +#include "audiohw.h" +#include "sound.h" +#include "nwzlinux_codec.h" + +int audio_channels = 2; +int audio_output_source = AUDIO_SRC_PLAYBACK; + +void audio_set_output_source(int source) +{ + if((unsigned)source >= AUDIO_NUM_SOURCES) + source = AUDIO_SRC_PLAYBACK; + + audio_output_source = source; +} + +void audio_input_mux(int source, unsigned flags) +{ + static int last_source = AUDIO_SRC_PLAYBACK; + + (void)flags; + + switch (source) + { + default: /* playback - no recording */ + source = AUDIO_SRC_PLAYBACK; + case AUDIO_SRC_PLAYBACK: + audio_channels = 2; + if (source != last_source) + audiohw_set_playback_src(NWZ_PLAYBACK); + break; + + case AUDIO_SRC_FMRADIO: /* recording and playback */ + audio_channels = 2; + if (source == last_source) + break; + + audiohw_set_playback_src(NWZ_RADIO); + break; + } /* end switch */ + + last_source = source; +} /* audio_input_mux */ diff --git a/firmware/target/hosted/sonynwz/nwz_tuner.h b/firmware/target/hosted/sonynwz/nwz_tuner.h new file mode 100644 index 0000000000..36b1b9634c --- /dev/null +++ b/firmware/target/hosted/sonynwz/nwz_tuner.h @@ -0,0 +1,61 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2017 Amaury Pouly + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef __NWZ_TUNER_H__ +#define __NWZ_TUNER_H__ + +#define NWZ_TUNER_DEV "/dev/radio0" +/* we only describe the private ioctl, the rest is standard v4l2 */ +#define NWZ_TUNER_TYPE 'v' + +#define NWZ_TUNER_REG_COUNT 20 + +/* there is something slightly fishy about this structure, the ioctl code says it must be of size + * 0x2C but the disassembled code looks like it uses a size of 0x24, probably Sony's code is + * massively broken */ +struct nwz_tuner_ioctl_t +{ + union + { + uint32_t regno; // for GET_REG + uint32_t rssi; // for GET_RSSI + uint32_t freq; // for SET_FREQ + struct + { + uint8_t regno; + uint8_t reserved; + uint16_t val; + }put; // for PUT_REG + }; + uint16_t regs[NWZ_TUNER_REG_COUNT]; +} __attribute__((packed)); + +/* these requests seem to be the only one supported by all generations */ +#define NWZ_TUNER_INIT_REG _IOWR(NWZ_TUNER_TYPE, 41, struct nwz_tuner_ioctl_t) +#define NWZ_TUNER_PUT_REG _IOWR(NWZ_TUNER_TYPE, 42, struct nwz_tuner_ioctl_t) +#define NWZ_TUNER_GET_REG _IOWR(NWZ_TUNER_TYPE, 43, struct nwz_tuner_ioctl_t) +#define NWZ_TUNER_GET_REG_ALL _IOWR(NWZ_TUNER_TYPE, 44, struct nwz_tuner_ioctl_t) +#define NWZ_TUNER_SET_FREQ _IOWR(NWZ_TUNER_TYPE, 45, struct nwz_tuner_ioctl_t) +#define NWZ_TUNER_RESET _IOW(NWZ_TUNER_TYPE, 46, struct nwz_tuner_ioctl_t) +#define NWZ_TUNER_GPIO2_ON _IOW(NWZ_TUNER_TYPE, 47, struct nwz_tuner_ioctl_t) +#define NWZ_TUNER_GPIO2_OFF _IOW(NWZ_TUNER_TYPE, 48, struct nwz_tuner_ioctl_t) +#define NWZ_TUNER_GET_RSSI _IOWR(NWZ_TUNER_TYPE, 49, struct nwz_tuner_ioctl_t) + +#endif /* __NWZ_TUNER_H__ */ diff --git a/firmware/target/hosted/sonynwz/radio-nwz.c b/firmware/target/hosted/sonynwz/radio-nwz.c new file mode 100644 index 0000000000..db4ffda465 --- /dev/null +++ b/firmware/target/hosted/sonynwz/radio-nwz.c @@ -0,0 +1,110 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2017 Amaury Pouly + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include +#include +#include +#include +#include "stdint.h" +#include "stdio.h" +#include "string.h" +#include "kernel.h" +#include "rbendian.h" + +#include "nwz_tuner.h" +#include "si4700.h" +#include "power.h" + +static int radio_fd = -1; + +bool tuner_power(bool status) +{ + if(status != tuner_powered()) + { + if(status) + { + radio_fd = open(NWZ_TUNER_DEV, 0); + if(radio_fd != -1) + { + /* Sony is braindead, opening the radio device mutes all audio (even from the DAC) + * until SET_FREQ is called with a valid frequency, choose 90MHz because it is valid + * in all bands */ + struct nwz_tuner_ioctl_t req; + memset(&req, 0, sizeof(req)); // just to avoid garbage in + req.freq = 90000; + int ret = ioctl(radio_fd, NWZ_TUNER_SET_FREQ, &req); + if(ret != 0) + perror("tuner set_freq error"); + } + else + perror("tuner open error"); + } + else + { + close(radio_fd); + radio_fd = -1; + } + } + return tuner_powered(); +} + +bool tuner_powered(void) +{ + return radio_fd != -1; +} + +/* one can adjust the following depending on the tuner, for now it is calibrated for the si470x + * which is crazy, because it starts reading at register 0xA and writing at 0x2. We need to + * "emulate" this behavior... Note that Si470x transmits in big-endian */ +#define READ_START_REG 0xA +#define WRITE_START_REG 0x2 +#define REG_COUNT 16 +#define REG_SIZE 2 +#define REG_TYPE uint16_t +#define REG_SWAP swap16 + +int fmradio_i2c_write(unsigned char address, unsigned char* buf, int count) +{ + (void)address; + struct nwz_tuner_ioctl_t req; + memset(&req, 0, sizeof(req)); // just to avoid garbage in + for(int i = 0; i < count / REG_SIZE; i++) + { + req.put.regno = (WRITE_START_REG + i) % REG_COUNT; + req.put.val = REG_SWAP(*(REG_TYPE *)&buf[i * REG_SIZE]); + int ret = ioctl(radio_fd, NWZ_TUNER_PUT_REG, &req); + if(ret != 0) + return ret; + } + return 0; +} + +int fmradio_i2c_read(unsigned char address, unsigned char* buf, int count) +{ + (void)address; + struct nwz_tuner_ioctl_t req; + memset(&req, 0, sizeof(req)); // just to avoid garbage in + int ret = ioctl(radio_fd, NWZ_TUNER_GET_REG_ALL, &req); + if(ret != 0) + return ret; + for(int i = 0; i < count / REG_SIZE; i++) + *(REG_TYPE *)&buf[i * REG_SIZE] = REG_SWAP(req.regs[(READ_START_REG + i) % REG_COUNT]); + return 0; +} diff --git a/tools/configure b/tools/configure index bd5fba6607..0001ab30f5 100755 --- a/tools/configure +++ b/tools/configure @@ -3841,7 +3841,7 @@ fi output="rockbox.sony" boottool="$rootdir/tools/scramble -add=e350" bootoutput="bootloader-nwze350.sony" - appextra="gui:recorder" + appextra="gui:recorder:radio" plugins="" swcodec="yes" toolset=$genericbitmaptools @@ -3904,7 +3904,7 @@ fi output="rockbox.sony" boottool="$rootdir/tools/scramble -add=e450" bootoutput="bootloader-nwze450.sony" - appextra="gui:recorder" + appextra="gui:recorder:radio" plugins="" swcodec="yes" toolset=$genericbitmaptools @@ -3927,7 +3927,7 @@ fi output="rockbox.sony" boottool="$rootdir/tools/scramble -add=e460" bootoutput="bootloader-nwze460.sony" - appextra="gui:recorder" + appextra="gui:recorder:radio" plugins="yes" swcodec="yes" toolset=$genericbitmaptools @@ -3950,7 +3950,7 @@ fi output="rockbox.sony" boottool="$rootdir/tools/scramble -add=e470" bootoutput="bootloader-nwze470.sony" - appextra="gui:recorder" + appextra="gui:recorder:radio" plugins="" swcodec="yes" toolset=$genericbitmaptools @@ -3973,7 +3973,7 @@ fi output="rockbox.sony" boottool="$rootdir/tools/scramble -add=e580" bootoutput="bootloader-nwze580.sony" - appextra="gui:recorder" + appextra="gui:recorder:radio" plugins="" swcodec="yes" toolset=$genericbitmaptools @@ -3996,7 +3996,7 @@ fi output="rockbox.sony" boottool="$rootdir/tools/scramble -add=a10" bootoutput="bootloader-nwza10.sony" - appextra="gui:recorder" + appextra="gui:recorder:radio" plugins="" swcodec="yes" toolset=$genericbitmaptools @@ -4019,7 +4019,7 @@ fi output="rockbox.sony" boottool="$rootdir/tools/scramble -add=a20" bootoutput="bootloader-nwa20.sony" - appextra="gui:recorder" + appextra="gui:recorder:radio" plugins="" swcodec="yes" toolset=$genericbitmaptools @@ -4042,7 +4042,7 @@ fi output="rockbox.sony" boottool="$rootdir/tools/scramble -add=a860" bootoutput="bootloader-nwza860.sony" - appextra="gui:recorder" + appextra="gui:recorder:radio" plugins="" swcodec="yes" toolset=$genericbitmaptools @@ -4065,7 +4065,7 @@ fi output="rockbox.sony" boottool="$rootdir/tools/scramble -add=s750" bootoutput="bootloader-nwzs750.sony" - appextra="gui:recorder" + appextra="gui:recorder:radio" plugins="" swcodec="yes" toolset=$genericbitmaptools