From 1ba5ef716d37a9fe7f0863caed2fab8570b2c7c4 Mon Sep 17 00:00:00 2001 From: Cástor Muñoz Date: Fri, 3 Mar 2017 22:46:57 +0100 Subject: ipod6g: rename some target files As preparation to add new targets to the s5l8702 directory, rename files as: s5l8702/ipod6g/*-ipod6g.c -> s5l8702/ipod6g/*-6g.c Change-Id: I0cd03d6bcf39b2aa198235f9014cb6948bbafcd5 --- firmware/target/arm/s5l8702/ipod6g/adc-6g.c | 117 ++ firmware/target/arm/s5l8702/ipod6g/adc-ipod6g.c | 117 -- firmware/target/arm/s5l8702/ipod6g/audio-6g.c | 97 ++ firmware/target/arm/s5l8702/ipod6g/audio-ipod6g.c | 97 -- firmware/target/arm/s5l8702/ipod6g/backlight-6g.c | 69 ++ .../target/arm/s5l8702/ipod6g/backlight-ipod6g.c | 69 -- firmware/target/arm/s5l8702/ipod6g/cscodec-6g.c | 64 ++ .../target/arm/s5l8702/ipod6g/cscodec-ipod6g.c | 64 -- firmware/target/arm/s5l8702/ipod6g/lcd-6g.c | 578 ++++++++++ firmware/target/arm/s5l8702/ipod6g/lcd-asm-6g.S | 1013 ++++++++++++++++++ .../target/arm/s5l8702/ipod6g/lcd-asm-ipod6g.S | 1013 ------------------ firmware/target/arm/s5l8702/ipod6g/lcd-ipod6g.c | 578 ---------- firmware/target/arm/s5l8702/ipod6g/piezo-6g.c | 125 +++ firmware/target/arm/s5l8702/ipod6g/piezo-ipod6g.c | 125 --- firmware/target/arm/s5l8702/ipod6g/pmu-6g.c | 469 ++++++++ firmware/target/arm/s5l8702/ipod6g/pmu-ipod6g.c | 469 -------- firmware/target/arm/s5l8702/ipod6g/power-6g.c | 134 +++ firmware/target/arm/s5l8702/ipod6g/power-ipod6g.c | 134 --- firmware/target/arm/s5l8702/ipod6g/powermgmt-6g.c | 81 ++ .../target/arm/s5l8702/ipod6g/powermgmt-ipod6g.c | 81 -- firmware/target/arm/s5l8702/ipod6g/rtc-6g.c | 75 ++ firmware/target/arm/s5l8702/ipod6g/rtc-ipod6g.c | 75 -- firmware/target/arm/s5l8702/ipod6g/serial-6g.c | 249 +++++ firmware/target/arm/s5l8702/ipod6g/serial-ipod6g.c | 249 ----- .../target/arm/s5l8702/ipod6g/storage_ata-6g.c | 1131 ++++++++++++++++++++ .../target/arm/s5l8702/ipod6g/storage_ata-ipod6g.c | 1131 -------------------- 26 files changed, 4202 insertions(+), 4202 deletions(-) create mode 100644 firmware/target/arm/s5l8702/ipod6g/adc-6g.c delete mode 100644 firmware/target/arm/s5l8702/ipod6g/adc-ipod6g.c create mode 100644 firmware/target/arm/s5l8702/ipod6g/audio-6g.c delete mode 100644 firmware/target/arm/s5l8702/ipod6g/audio-ipod6g.c create mode 100644 firmware/target/arm/s5l8702/ipod6g/backlight-6g.c delete mode 100644 firmware/target/arm/s5l8702/ipod6g/backlight-ipod6g.c create mode 100644 firmware/target/arm/s5l8702/ipod6g/cscodec-6g.c delete mode 100644 firmware/target/arm/s5l8702/ipod6g/cscodec-ipod6g.c create mode 100644 firmware/target/arm/s5l8702/ipod6g/lcd-6g.c create mode 100644 firmware/target/arm/s5l8702/ipod6g/lcd-asm-6g.S delete mode 100644 firmware/target/arm/s5l8702/ipod6g/lcd-asm-ipod6g.S delete mode 100644 firmware/target/arm/s5l8702/ipod6g/lcd-ipod6g.c create mode 100644 firmware/target/arm/s5l8702/ipod6g/piezo-6g.c delete mode 100644 firmware/target/arm/s5l8702/ipod6g/piezo-ipod6g.c create mode 100644 firmware/target/arm/s5l8702/ipod6g/pmu-6g.c delete mode 100644 firmware/target/arm/s5l8702/ipod6g/pmu-ipod6g.c create mode 100644 firmware/target/arm/s5l8702/ipod6g/power-6g.c delete mode 100644 firmware/target/arm/s5l8702/ipod6g/power-ipod6g.c create mode 100644 firmware/target/arm/s5l8702/ipod6g/powermgmt-6g.c delete mode 100644 firmware/target/arm/s5l8702/ipod6g/powermgmt-ipod6g.c create mode 100644 firmware/target/arm/s5l8702/ipod6g/rtc-6g.c delete mode 100644 firmware/target/arm/s5l8702/ipod6g/rtc-ipod6g.c create mode 100644 firmware/target/arm/s5l8702/ipod6g/serial-6g.c delete mode 100644 firmware/target/arm/s5l8702/ipod6g/serial-ipod6g.c create mode 100644 firmware/target/arm/s5l8702/ipod6g/storage_ata-6g.c delete mode 100644 firmware/target/arm/s5l8702/ipod6g/storage_ata-ipod6g.c (limited to 'firmware/target') diff --git a/firmware/target/arm/s5l8702/ipod6g/adc-6g.c b/firmware/target/arm/s5l8702/ipod6g/adc-6g.c new file mode 100644 index 0000000000..fbe5ef805e --- /dev/null +++ b/firmware/target/arm/s5l8702/ipod6g/adc-6g.c @@ -0,0 +1,117 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: adc-s5l8700.c 21775 2009-07-11 14:12:02Z bertrik $ + * + * Copyright (C) 2009 by Bertrik Sikken + * + * 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 "inttypes.h" +#include "s5l8702.h" +#include "adc.h" +#include "adc-target.h" +#include "pmu-target.h" +#include "kernel.h" + + +/* MS_TO_TICKS converts a milisecond time period into the + * corresponding amount of ticks. If the time period cannot + * be accurately measured in ticks it will round up. + */ +#define MS_PER_HZ (1000/HZ) +#define MS_TO_TICKS(x) (((x)+MS_PER_HZ-1)/MS_PER_HZ) + +static const struct pmu_adc_channel adc_channels[] = +{ + [ADC_BATTERY] = + { + .name = "Battery", + .adcc1 = PCF5063X_ADCC1_MUX_BATSNS_SUBTR + | PCF5063X_ADCC1_AVERAGE_4 + | PCF5063X_ADCC1_RES_10BIT, + }, + + [ADC_USBDATA] = + { + .name = "USB D+/D-", + .adcc1 = PCF5063X_ADCC1_MUX_ADCIN2_RES + | PCF5063X_ADCC1_AVERAGE_16 + | PCF5063X_ADCC1_RES_10BIT, + .adcc3 = PCF5063X_ADCC3_RES_DIV_THREE, + }, + + [ADC_ACCESSORY] = + { + .name = "Accessory", + .adcc1 = PCF5063X_ADCC1_MUX_ADCIN1 + | PCF5063X_ADCC1_AVERAGE_16 + | PCF5063X_ADCC1_RES_10BIT, + .adcc2 = PCF5063X_ADCC2_RATIO_ADCIN1 + | PCF5063X_ADCC2_RATIOSETTL_10US, + .adcc3 = PCF5063X_ADCC3_ACCSW_EN, + .bias_dly = MS_TO_TICKS(50), + }, +}; + +const char *adc_name(int channel) +{ + return adc_channels[channel].name; +} + +unsigned short adc_read_millivolts(int channel) +{ + const struct pmu_adc_channel *ch = &adc_channels[channel]; + return pmu_adc_raw2mv(ch, pmu_read_adc(ch)); +} + +/* Returns battery voltage [millivolts] */ +unsigned int adc_read_battery_voltage(void) +{ + return adc_read_millivolts(ADC_BATTERY); +} + +/* Returns USB D+/D- voltage from ADC [millivolts] */ +unsigned int adc_read_usbdata_voltage(bool dp) +{ + unsigned int mvolts; + int gpio = dp ? 0xb0300 : 0xb0200; /* select D+/D- */ + GPIOCMD = gpio | 0xf; /* route to ADCIN2 */ + mvolts = adc_read_millivolts(ADC_USBDATA); + GPIOCMD = gpio | 0xe; /* unroute */ + return mvolts; +} + +/* Returns resistor connected to "Accessory identify" pin [ohms] */ +#define IAP_DEVICE_RESISTOR 100000 /* ohms */ +int adc_read_accessory_resistor(void) +{ + int raw = pmu_read_adc(&adc_channels[ADC_ACCESSORY]); + return (1023-raw) ? raw * IAP_DEVICE_RESISTOR / (1023-raw) + : -1 /* open circuit */; +} + + +/* API functions */ +unsigned short adc_read(int channel) +{ + return pmu_read_adc(&adc_channels[channel]); +} + +void adc_init(void) +{ +} diff --git a/firmware/target/arm/s5l8702/ipod6g/adc-ipod6g.c b/firmware/target/arm/s5l8702/ipod6g/adc-ipod6g.c deleted file mode 100644 index fbe5ef805e..0000000000 --- a/firmware/target/arm/s5l8702/ipod6g/adc-ipod6g.c +++ /dev/null @@ -1,117 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id: adc-s5l8700.c 21775 2009-07-11 14:12:02Z bertrik $ - * - * Copyright (C) 2009 by Bertrik Sikken - * - * 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 "inttypes.h" -#include "s5l8702.h" -#include "adc.h" -#include "adc-target.h" -#include "pmu-target.h" -#include "kernel.h" - - -/* MS_TO_TICKS converts a milisecond time period into the - * corresponding amount of ticks. If the time period cannot - * be accurately measured in ticks it will round up. - */ -#define MS_PER_HZ (1000/HZ) -#define MS_TO_TICKS(x) (((x)+MS_PER_HZ-1)/MS_PER_HZ) - -static const struct pmu_adc_channel adc_channels[] = -{ - [ADC_BATTERY] = - { - .name = "Battery", - .adcc1 = PCF5063X_ADCC1_MUX_BATSNS_SUBTR - | PCF5063X_ADCC1_AVERAGE_4 - | PCF5063X_ADCC1_RES_10BIT, - }, - - [ADC_USBDATA] = - { - .name = "USB D+/D-", - .adcc1 = PCF5063X_ADCC1_MUX_ADCIN2_RES - | PCF5063X_ADCC1_AVERAGE_16 - | PCF5063X_ADCC1_RES_10BIT, - .adcc3 = PCF5063X_ADCC3_RES_DIV_THREE, - }, - - [ADC_ACCESSORY] = - { - .name = "Accessory", - .adcc1 = PCF5063X_ADCC1_MUX_ADCIN1 - | PCF5063X_ADCC1_AVERAGE_16 - | PCF5063X_ADCC1_RES_10BIT, - .adcc2 = PCF5063X_ADCC2_RATIO_ADCIN1 - | PCF5063X_ADCC2_RATIOSETTL_10US, - .adcc3 = PCF5063X_ADCC3_ACCSW_EN, - .bias_dly = MS_TO_TICKS(50), - }, -}; - -const char *adc_name(int channel) -{ - return adc_channels[channel].name; -} - -unsigned short adc_read_millivolts(int channel) -{ - const struct pmu_adc_channel *ch = &adc_channels[channel]; - return pmu_adc_raw2mv(ch, pmu_read_adc(ch)); -} - -/* Returns battery voltage [millivolts] */ -unsigned int adc_read_battery_voltage(void) -{ - return adc_read_millivolts(ADC_BATTERY); -} - -/* Returns USB D+/D- voltage from ADC [millivolts] */ -unsigned int adc_read_usbdata_voltage(bool dp) -{ - unsigned int mvolts; - int gpio = dp ? 0xb0300 : 0xb0200; /* select D+/D- */ - GPIOCMD = gpio | 0xf; /* route to ADCIN2 */ - mvolts = adc_read_millivolts(ADC_USBDATA); - GPIOCMD = gpio | 0xe; /* unroute */ - return mvolts; -} - -/* Returns resistor connected to "Accessory identify" pin [ohms] */ -#define IAP_DEVICE_RESISTOR 100000 /* ohms */ -int adc_read_accessory_resistor(void) -{ - int raw = pmu_read_adc(&adc_channels[ADC_ACCESSORY]); - return (1023-raw) ? raw * IAP_DEVICE_RESISTOR / (1023-raw) - : -1 /* open circuit */; -} - - -/* API functions */ -unsigned short adc_read(int channel) -{ - return pmu_read_adc(&adc_channels[channel]); -} - -void adc_init(void) -{ -} diff --git a/firmware/target/arm/s5l8702/ipod6g/audio-6g.c b/firmware/target/arm/s5l8702/ipod6g/audio-6g.c new file mode 100644 index 0000000000..6a3bab06d6 --- /dev/null +++ b/firmware/target/arm/s5l8702/ipod6g/audio-6g.c @@ -0,0 +1,97 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: audio-nano2g.c 23095 2009-10-11 09:17:12Z dave $ + * + * Copyright (C) 2006 by Michael Sevakis + * + * 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 "system.h" +#include "cpu.h" +#include "audio.h" +#include "sound.h" +#include "pmu-target.h" + +extern int rec_hw_ver; + +#if INPUT_SRC_CAPS != 0 +void audio_set_output_source(int source) +{ + if ((unsigned)source >= AUDIO_NUM_SOURCES) + source = AUDIO_SRC_PLAYBACK; +} /* audio_set_output_source */ + +void audio_input_mux(int source, unsigned flags) +{ + (void)flags; + /* Prevent pops from unneeded switching */ + static int last_source = AUDIO_SRC_PLAYBACK; + + switch (source) + { + default: /* playback - no recording */ + source = AUDIO_SRC_PLAYBACK; + case AUDIO_SRC_PLAYBACK: +#ifdef HAVE_RECORDING + if (source != last_source) + { + audiohw_set_monitor(false); + audiohw_disable_recording(); + + /* Vcodec = 1800mV (900mV + value*100mV) */ + pmu_ldo_set_voltage(3, 0x9); + + if (rec_hw_ver == 1) + GPIOCMD = 0xe070e; + } +#endif + break; + +#ifdef HAVE_MIC_REC + case AUDIO_SRC_MIC: /* recording only */ + if (source != last_source) + { + if (rec_hw_ver == 1) + GPIOCMD = 0xe070f; + + /* Vcodec = 2400mV (900mV + value*100mV) */ + pmu_ldo_set_voltage(3, 0xf); + + audiohw_set_monitor(false); + audiohw_enable_recording(true); /* source mic */ + } + break; +#endif + +#ifdef HAVE_LINE_REC + case AUDIO_SRC_LINEIN: /* recording only */ + if (source != last_source) + { + if (rec_hw_ver == 1) + GPIOCMD = 0xe070e; + + /* Vcodec = 2400mV (900mV + value*100mV) */ + pmu_ldo_set_voltage(3, 0xf); + + audiohw_set_monitor(false); + audiohw_enable_recording(false); /* source line */ + } + break; +#endif + } /* end switch */ + + last_source = source; +} /* audio_input_mux */ +#endif /* INPUT_SRC_CAPS != 0 */ diff --git a/firmware/target/arm/s5l8702/ipod6g/audio-ipod6g.c b/firmware/target/arm/s5l8702/ipod6g/audio-ipod6g.c deleted file mode 100644 index 6a3bab06d6..0000000000 --- a/firmware/target/arm/s5l8702/ipod6g/audio-ipod6g.c +++ /dev/null @@ -1,97 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id: audio-nano2g.c 23095 2009-10-11 09:17:12Z dave $ - * - * Copyright (C) 2006 by Michael Sevakis - * - * 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 "system.h" -#include "cpu.h" -#include "audio.h" -#include "sound.h" -#include "pmu-target.h" - -extern int rec_hw_ver; - -#if INPUT_SRC_CAPS != 0 -void audio_set_output_source(int source) -{ - if ((unsigned)source >= AUDIO_NUM_SOURCES) - source = AUDIO_SRC_PLAYBACK; -} /* audio_set_output_source */ - -void audio_input_mux(int source, unsigned flags) -{ - (void)flags; - /* Prevent pops from unneeded switching */ - static int last_source = AUDIO_SRC_PLAYBACK; - - switch (source) - { - default: /* playback - no recording */ - source = AUDIO_SRC_PLAYBACK; - case AUDIO_SRC_PLAYBACK: -#ifdef HAVE_RECORDING - if (source != last_source) - { - audiohw_set_monitor(false); - audiohw_disable_recording(); - - /* Vcodec = 1800mV (900mV + value*100mV) */ - pmu_ldo_set_voltage(3, 0x9); - - if (rec_hw_ver == 1) - GPIOCMD = 0xe070e; - } -#endif - break; - -#ifdef HAVE_MIC_REC - case AUDIO_SRC_MIC: /* recording only */ - if (source != last_source) - { - if (rec_hw_ver == 1) - GPIOCMD = 0xe070f; - - /* Vcodec = 2400mV (900mV + value*100mV) */ - pmu_ldo_set_voltage(3, 0xf); - - audiohw_set_monitor(false); - audiohw_enable_recording(true); /* source mic */ - } - break; -#endif - -#ifdef HAVE_LINE_REC - case AUDIO_SRC_LINEIN: /* recording only */ - if (source != last_source) - { - if (rec_hw_ver == 1) - GPIOCMD = 0xe070e; - - /* Vcodec = 2400mV (900mV + value*100mV) */ - pmu_ldo_set_voltage(3, 0xf); - - audiohw_set_monitor(false); - audiohw_enable_recording(false); /* source line */ - } - break; -#endif - } /* end switch */ - - last_source = source; -} /* audio_input_mux */ -#endif /* INPUT_SRC_CAPS != 0 */ diff --git a/firmware/target/arm/s5l8702/ipod6g/backlight-6g.c b/firmware/target/arm/s5l8702/ipod6g/backlight-6g.c new file mode 100644 index 0000000000..7d1c8bde13 --- /dev/null +++ b/firmware/target/arm/s5l8702/ipod6g/backlight-6g.c @@ -0,0 +1,69 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: backlight-nano2g.c 28601 2010-11-14 20:39:18Z theseven $ + * + * Copyright (C) 2009 by Dave Chapman + * + * 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 "config.h" +#include "kernel.h" +#include "backlight.h" +#include "backlight-target.h" +#include "pmu-target.h" + +#ifdef HAVE_LCD_SLEEP +bool lcd_active(void); +void lcd_awake(void); +void lcd_update(void); +#endif + +void backlight_hw_brightness(int brightness) +{ + pmu_write(0x28, brightness); +} + +void backlight_hw_on(void) +{ +#ifdef HAVE_LCD_SLEEP + if (!lcd_active()) + { + lcd_awake(); + lcd_update(); + sleep(HZ/20); + } +#endif + pmu_write(0x29, 1); +} + +void backlight_hw_off(void) +{ + pmu_write(0x29, 0); +} + +bool backlight_hw_init(void) +{ + /* LEDCTL: overvoltage protection enabled, OCP limit is 500mA */ + pmu_write(0x2a, 0x05); + + pmu_write(0x2b, 0x14); /* T_dimstep = 16 * value / 32768 */ + backlight_hw_brightness(DEFAULT_BRIGHTNESS_SETTING); + + backlight_hw_on(); + + return true; +} diff --git a/firmware/target/arm/s5l8702/ipod6g/backlight-ipod6g.c b/firmware/target/arm/s5l8702/ipod6g/backlight-ipod6g.c deleted file mode 100644 index 7d1c8bde13..0000000000 --- a/firmware/target/arm/s5l8702/ipod6g/backlight-ipod6g.c +++ /dev/null @@ -1,69 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id: backlight-nano2g.c 28601 2010-11-14 20:39:18Z theseven $ - * - * Copyright (C) 2009 by Dave Chapman - * - * 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 "config.h" -#include "kernel.h" -#include "backlight.h" -#include "backlight-target.h" -#include "pmu-target.h" - -#ifdef HAVE_LCD_SLEEP -bool lcd_active(void); -void lcd_awake(void); -void lcd_update(void); -#endif - -void backlight_hw_brightness(int brightness) -{ - pmu_write(0x28, brightness); -} - -void backlight_hw_on(void) -{ -#ifdef HAVE_LCD_SLEEP - if (!lcd_active()) - { - lcd_awake(); - lcd_update(); - sleep(HZ/20); - } -#endif - pmu_write(0x29, 1); -} - -void backlight_hw_off(void) -{ - pmu_write(0x29, 0); -} - -bool backlight_hw_init(void) -{ - /* LEDCTL: overvoltage protection enabled, OCP limit is 500mA */ - pmu_write(0x2a, 0x05); - - pmu_write(0x2b, 0x14); /* T_dimstep = 16 * value / 32768 */ - backlight_hw_brightness(DEFAULT_BRIGHTNESS_SETTING); - - backlight_hw_on(); - - return true; -} diff --git a/firmware/target/arm/s5l8702/ipod6g/cscodec-6g.c b/firmware/target/arm/s5l8702/ipod6g/cscodec-6g.c new file mode 100644 index 0000000000..41531de4e3 --- /dev/null +++ b/firmware/target/arm/s5l8702/ipod6g/cscodec-6g.c @@ -0,0 +1,64 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: wmcodec-s5l8700.c 22025 2009-07-25 00:49:13Z dave $ + * + * S5L8702-specific code for Cirrus codecs + * + * Copyright (c) 2010 Michael Sparmann + * + * 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 "system.h" +#include "audiohw.h" +#include "i2c-s5l8702.h" +#include "s5l8702.h" +#include "cscodec.h" + +void audiohw_init(void) +{ +#ifdef HAVE_CS42L55 + audiohw_preinit(); +#endif +} + +unsigned char cscodec_read(int reg) +{ + unsigned char data; + i2c_read(0, 0x94, reg, 1, &data); + return data; +} + +void cscodec_write(int reg, unsigned char data) +{ + i2c_write(0, 0x94, reg, 1, &data); +} + +void cscodec_power(bool state) +{ + (void)state; //TODO: Figure out which LDO this is +} + +void cscodec_reset(bool state) +{ + if (state) PDAT(3) &= ~8; + else PDAT(3) |= 8; +} + +void cscodec_clock(bool state) +{ + if (state) CLKCON3 &= ~0xffff; + else CLKCON3 |= 0x8000; +} diff --git a/firmware/target/arm/s5l8702/ipod6g/cscodec-ipod6g.c b/firmware/target/arm/s5l8702/ipod6g/cscodec-ipod6g.c deleted file mode 100644 index 41531de4e3..0000000000 --- a/firmware/target/arm/s5l8702/ipod6g/cscodec-ipod6g.c +++ /dev/null @@ -1,64 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id: wmcodec-s5l8700.c 22025 2009-07-25 00:49:13Z dave $ - * - * S5L8702-specific code for Cirrus codecs - * - * Copyright (c) 2010 Michael Sparmann - * - * 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 "system.h" -#include "audiohw.h" -#include "i2c-s5l8702.h" -#include "s5l8702.h" -#include "cscodec.h" - -void audiohw_init(void) -{ -#ifdef HAVE_CS42L55 - audiohw_preinit(); -#endif -} - -unsigned char cscodec_read(int reg) -{ - unsigned char data; - i2c_read(0, 0x94, reg, 1, &data); - return data; -} - -void cscodec_write(int reg, unsigned char data) -{ - i2c_write(0, 0x94, reg, 1, &data); -} - -void cscodec_power(bool state) -{ - (void)state; //TODO: Figure out which LDO this is -} - -void cscodec_reset(bool state) -{ - if (state) PDAT(3) &= ~8; - else PDAT(3) |= 8; -} - -void cscodec_clock(bool state) -{ - if (state) CLKCON3 &= ~0xffff; - else CLKCON3 |= 0x8000; -} diff --git a/firmware/target/arm/s5l8702/ipod6g/lcd-6g.c b/firmware/target/arm/s5l8702/ipod6g/lcd-6g.c new file mode 100644 index 0000000000..14647a5697 --- /dev/null +++ b/firmware/target/arm/s5l8702/ipod6g/lcd-6g.c @@ -0,0 +1,578 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: lcd-nano2g.c 28868 2010-12-21 06:59:17Z Buschel $ + * + * Copyright (C) 2009 by Dave Chapman + * + * 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 "hwcompat.h" +#include "kernel.h" +#include "lcd.h" +#include "system.h" +#include "cpu.h" +#include "pmu-target.h" +#include "power.h" +#include "string.h" +#include "dma-s5l8702.h" + + +#define R_HORIZ_GRAM_ADDR_SET 0x200 +#define R_VERT_GRAM_ADDR_SET 0x201 +#define R_WRITE_DATA_TO_GRAM 0x202 +#define R_HORIZ_ADDR_START_POS 0x210 +#define R_HORIZ_ADDR_END_POS 0x211 +#define R_VERT_ADDR_START_POS 0x212 +#define R_VERT_ADDR_END_POS 0x213 + + +/* LCD type 1 register defines */ + +#define R_COLUMN_ADDR_SET 0x2a +#define R_ROW_ADDR_SET 0x2b +#define R_MEMORY_WRITE 0x2c + + +/** globals **/ + +int lcd_type; /* also needed in debug-s5l8702.c */ +static struct mutex lcd_mutex; +static uint16_t lcd_dblbuf[LCD_HEIGHT][LCD_WIDTH] CACHEALIGN_ATTR; +static bool lcd_ispowered; + +#define CMD 0 /* send command with N data */ +#define MREG 1 /* write multiple registers */ +#define SLEEP 2 +#define END 0xff + +#define CMD16(len) (CMD | ((len) << 8)) +#define MREG16(len) (MREG | ((len) << 8)) +#define SLEEP16(t) (SLEEP | ((t) << 8)) + +/* powersave sequences */ + +static const unsigned char lcd_sleep_seq_01[] = +{ + CMD, 0x28, 0, /* Display Off */ + SLEEP, 5, /* 50 ms */ + CMD, 0x10, 0, /* Sleep In Mode */ + SLEEP, 5, /* 50 ms */ + END +}; + +static const unsigned short lcd_enter_deepstby_seq_23[] = +{ + /* Display Off */ + MREG16(1), 0x007, 0x0172, + MREG16(1), 0x030, 0x03ff, + SLEEP16(9), + MREG16(1), 0x007, 0x0120, + MREG16(1), 0x030, 0x0000, + MREG16(1), 0x100, 0x0780, + MREG16(1), 0x007, 0x0000, + MREG16(1), 0x101, 0x0260, + MREG16(1), 0x102, 0x00a9, + SLEEP16(3), + MREG16(1), 0x100, 0x0700, + + /* Deep Standby Mode */ + MREG16(1), 0x100, 0x0704, + SLEEP16(5), + END +}; + +/* init sequences */ + +#ifdef HAVE_LCD_SLEEP +static const unsigned char lcd_awake_seq_01[] = +{ + CMD, 0x11, 0, /* Sleep Out Mode */ + SLEEP, 6, /* 60 ms */ + CMD, 0x29, 0, /* Display On */ + END +}; +#endif + +#if defined(HAVE_LCD_SLEEP) || defined(BOOTLOADER) +static const unsigned short lcd_init_seq_23[] = +{ + /* Display settings */ + MREG16(1), 0x008, 0x0808, + MREG16(7), 0x010, 0x0013, 0x0300, 0x0101, 0x0a03, 0x0a0e, 0x0a19, 0x2402, + MREG16(1), 0x018, 0x0001, + MREG16(1), 0x090, 0x0021, + + /* Gamma settings */ + MREG16(14), 0x300, 0x0307, 0x0003, 0x0402, 0x0303, 0x0300, 0x0407, 0x1c04, + 0x0307, 0x0003, 0x0402, 0x0303, 0x0300, 0x0407, 0x1c04, + MREG16(14), 0x310, 0x0707, 0x0407, 0x0306, 0x0303, 0x0300, 0x0407, 0x1c01, + 0x0707, 0x0407, 0x0306, 0x0303, 0x0300, 0x0407, 0x1c01, + MREG16(14), 0x320, 0x0206, 0x0102, 0x0404, 0x0303, 0x0300, 0x0407, 0x1c1f, + 0x0206, 0x0102, 0x0404, 0x0303, 0x0300, 0x0407, 0x1c1f, + + /* GRAM and Base Imagen settings (ili9326ds) */ + MREG16(2), 0x400, 0x001d, 0x0001, + MREG16(1), 0x205, 0x0060, + + /* Power settings */ + MREG16(1), 0x007, 0x0001, + MREG16(1), 0x031, 0x0071, + MREG16(1), 0x110, 0x0001, + MREG16(6), 0x100, 0x17b0, 0x0220, 0x00bd, 0x1500, 0x0103, 0x0105, + + /* Display On */ + MREG16(1), 0x007, 0x0021, + MREG16(1), 0x001, 0x0110, + MREG16(1), 0x003, 0x0230, + MREG16(1), 0x002, 0x0500, + MREG16(1), 0x007, 0x0031, + MREG16(1), 0x030, 0x0007, + SLEEP16(3), + MREG16(1), 0x030, 0x03ff, + SLEEP16(6), + MREG16(1), 0x007, 0x0072, + SLEEP16(15), + MREG16(1), 0x007, 0x0173, + END +}; +#endif /* HAVE_LCD_SLEEP || BOOTLOADER */ + +#ifdef BOOTLOADER +static const unsigned char lcd_init_seq_0[] = +{ + CMD, 0x11, 0, /* Sleep Out Mode */ + SLEEP, 0x03, /* 30 ms */ + CMD, 0x35, 1, 0x00, /* TEON (TBC) */ + CMD, 0x3a, 1, 0x06, /* COLMOD (TBC) */ + CMD, 0x36, 1, 0x00, /* MADCTR (TBC) */ + CMD, 0x13, 0, /* NORON: Normal Mode On (Partial + Mode Off, Scroll Mode Off) */ + CMD, 0x29, 0, /* Display On */ + END +}; + +static const unsigned char lcd_init_seq_1[] = +{ + CMD, 0xb0, 21, 0x3a, 0x3a, 0x80, 0x80, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x3c, 0x30, 0x0f, 0x00, + 0x01, 0x54, 0x06, 0x66, 0x66, + CMD, 0xb8, 1, 0xd8, + CMD, 0xb1, 30, 0x14, 0x59, 0x00, 0x15, 0x57, 0x27, 0x04, 0x85, + 0x14, 0x59, 0x00, 0x15, 0x57, 0x27, 0x04, 0x85, + 0x14, 0x09, 0x15, 0x57, 0x27, 0x04, 0x05, + 0x14, 0x09, 0x15, 0x57, 0x27, 0x04, 0x05, + CMD, 0xd2, 1, 0x01, + + /* Gamma settings (TBC) */ + CMD, 0xe0, 13, 0x00, 0x00, 0x00, 0x05, 0x0b, 0x12, 0x16, 0x1f, + 0x25, 0x22, 0x24, 0x29, 0x1c, + CMD, 0xe1, 13, 0x08, 0x01, 0x01, 0x06, 0x0b, 0x11, 0x15, 0x1f, + 0x27, 0x26, 0x29, 0x2f, 0x1e, + CMD, 0xe2, 13, 0x07, 0x01, 0x01, 0x05, 0x09, 0x0f, 0x13, 0x1e, + 0x26, 0x25, 0x28, 0x2e, 0x1e, + CMD, 0xe3, 13, 0x00, 0x00, 0x00, 0x05, 0x0b, 0x12, 0x16, 0x1f, + 0x25, 0x22, 0x24, 0x29, 0x1c, + CMD, 0xe4, 13, 0x08, 0x01, 0x01, 0x06, 0x0b, 0x11, 0x15, 0x1f, + 0x27, 0x26, 0x29, 0x2f, 0x1e, + CMD, 0xe5, 13, 0x07, 0x01, 0x01, 0x05, 0x09, 0x0f, 0x13, 0x1e, + 0x26, 0x25, 0x28, 0x2e, 0x1e, + + CMD, 0x3a, 1, 0x06, /* COLMOD (TBC) */ + CMD, 0xc2, 1, 0x00, /* Power Control 3 (TBC) */ + CMD, 0x35, 1, 0x00, /* TEON (TBC) */ + CMD, 0x11, 0, /* Sleep Out Mode */ + SLEEP, 0x06, /* 60 ms */ + CMD, 0x13, 0, /* NORON: Normal Mode On (Partial + Mode Off, Scroll Mode Off) */ + CMD, 0x29, 0, /* Display On */ + END +}; +#endif + +/* DMA configuration */ + +/* one single transfer at once, needed LLIs: + * screen_size / (DMAC_LLI_MAX_COUNT << swidth) = + * (320*240*2) / (4095*2) = 19 + */ +#define LCD_DMA_TSKBUF_SZ 1 /* N tasks, MUST be pow2 */ +#define LCD_DMA_LLIBUF_SZ 32 /* N LLIs, MUST be pow2 */ + +static struct dmac_tsk lcd_dma_tskbuf[LCD_DMA_TSKBUF_SZ]; +static struct dmac_lli volatile \ + lcd_dma_llibuf[LCD_DMA_LLIBUF_SZ] CACHEALIGN_ATTR; + +static struct dmac_ch lcd_dma_ch = { + .dmac = &s5l8702_dmac0, + .prio = DMAC_CH_PRIO(4), + .cb_fn = NULL, + + .tskbuf = lcd_dma_tskbuf, + .tskbuf_mask = LCD_DMA_TSKBUF_SZ - 1, + .queue_mode = QUEUE_NORMAL, + + .llibuf = lcd_dma_llibuf, + .llibuf_mask = LCD_DMA_LLIBUF_SZ - 1, + .llibuf_bus = DMAC_MASTER_AHB1, +}; + +static struct dmac_ch_cfg lcd_dma_ch_cfg = { + .srcperi = S5L8702_DMAC0_PERI_MEM, + .dstperi = S5L8702_DMAC0_PERI_LCD_WR, + .sbsize = DMACCxCONTROL_BSIZE_1, + .dbsize = DMACCxCONTROL_BSIZE_1, + .swidth = DMACCxCONTROL_WIDTH_16, + .dwidth = DMACCxCONTROL_WIDTH_16, + .sbus = DMAC_MASTER_AHB1, + .dbus = DMAC_MASTER_AHB1, + .sinc = DMACCxCONTROL_INC_ENABLE, + .dinc = DMACCxCONTROL_INC_DISABLE, + .prot = DMAC_PROT_CACH | DMAC_PROT_BUFF | DMAC_PROT_PRIV, + .lli_xfer_max_count = DMAC_LLI_MAX_COUNT, +}; + +static inline void s5l_lcd_write_reg(int cmd, unsigned int data) +{ + while (LCD_STATUS & 0x10); + LCD_WCMD = cmd; + + while (LCD_STATUS & 0x10); + /* 16-bit/1-transfer data format (ili9320ds s7.2.2) */ + LCD_WDATA = (data & 0x78ff) | + ((data & 0x0300) << 1) | ((data & 0x0400) << 5); +} + +static inline void s5l_lcd_write_cmd(unsigned short cmd) +{ + while (LCD_STATUS & 0x10); + LCD_WCMD = cmd; +} + +static inline void s5l_lcd_write_data(unsigned short data) +{ + while (LCD_STATUS & 0x10); + LCD_WDATA = data; +} + +static void lcd_run_seq8(const unsigned char *seq) +{ + int n; + + while (1) switch (*seq++) + { + case CMD: + s5l_lcd_write_cmd(*seq++); + n = *seq++; + while (n--) + s5l_lcd_write_data(*seq++); + break; + case SLEEP: + sleep(*seq++); + break; + case END: + default: + /* bye */ + return; + } +} + +static void lcd_run_seq16(const unsigned short *seq) +{ + int action, param; + unsigned short reg; + + while (1) + { + action = *seq & 0xff; + param = *seq++ >> 8; + + switch (action) + { + case MREG: + reg = *seq++; + while (param--) + s5l_lcd_write_reg(reg++, *seq++); + break; + case SLEEP: + sleep(param); + break; + case END: + default: + /* bye */ + return; + } + } +} + +/*** hardware configuration ***/ + +int lcd_default_contrast(void) +{ + return 0x1f; +} + +void lcd_set_contrast(int val) +{ + (void)val; +} + +void lcd_set_invert_display(bool yesno) +{ + (void)yesno; +} + +void lcd_set_flip(bool yesno) +{ + (void)yesno; +} + +bool lcd_active(void) +{ + return lcd_ispowered; +} + +void lcd_powersave(void) +{ + mutex_lock(&lcd_mutex); + + if (lcd_type & 2) + lcd_run_seq16(lcd_enter_deepstby_seq_23); + else + lcd_run_seq8(lcd_sleep_seq_01); + + /* mask lcd controller clock gate */ + PWRCON(0) |= (1 << CLOCKGATE_LCD); + + lcd_ispowered = false; + + mutex_unlock(&lcd_mutex); +} + +void lcd_shutdown(void) +{ + pmu_write(0x2b, 0); /* Kill the backlight, instantly. */ + pmu_write(0x29, 0); + + lcd_powersave(); +} + +#ifdef HAVE_LCD_SLEEP +void lcd_sleep(void) +{ + lcd_powersave(); +} + +void lcd_awake(void) +{ + mutex_lock(&lcd_mutex); + + /* unmask lcd controller clock gate */ + PWRCON(0) &= ~(1 << CLOCKGATE_LCD); + + if (lcd_type & 2) { + /* release from deep standby mode (ili9320ds s12.3) */ + for (int i = 0; i < 6; i++) { + s5l_lcd_write_cmd(0x000); + udelay(1000); + } + + lcd_run_seq16(lcd_init_seq_23); + } + else + lcd_run_seq8(lcd_awake_seq_01); + + lcd_ispowered = true; + + mutex_unlock(&lcd_mutex); + + send_event(LCD_EVENT_ACTIVATION, NULL); +} +#endif + +/* LCD init */ +void lcd_init_device(void) +{ + mutex_init(&lcd_mutex); + + /* unmask lcd controller clock gate */ + PWRCON(0) &= ~(1 << CLOCKGATE_LCD); + + /* Detect lcd type */ + lcd_type = (PDAT6 & 0x30) >> 4; + + while (!(LCD_STATUS & 0x2)); + LCD_CONFIG = 0x80100db0; + LCD_PHTIME = 0x33; + + /* Configure DMA channel */ + dmac_ch_init(&lcd_dma_ch, &lcd_dma_ch_cfg); + +#ifdef BOOTLOADER + switch (lcd_type) { + case 0: lcd_run_seq8(lcd_init_seq_0); break; + case 1: lcd_run_seq8(lcd_init_seq_1); break; + default: lcd_run_seq16(lcd_init_seq_23); break; + } +#endif + + lcd_ispowered = true; +} + +/*** Update functions ***/ + +static inline void lcd_write_pixel(fb_data pixel) +{ + mutex_lock(&lcd_mutex); + LCD_WDATA = pixel; + mutex_unlock(&lcd_mutex); +} + +/* Update the display. + This must be called after all other LCD functions that change the display. */ +void lcd_update(void) ICODE_ATTR; +void lcd_update(void) +{ + lcd_update_rect(0, 0, LCD_WIDTH, LCD_HEIGHT); +} + +/* Line write helper function. */ +extern void lcd_write_line(const fb_data *addr, + int pixelcount, + const unsigned int lcd_base_addr); + +static void displaylcd_setup(int x, int y, int width, int height) ICODE_ATTR; +static void displaylcd_setup(int x, int y, int width, int height) +{ + /* TODO: ISR()->panicf()->lcd_update() blocks forever */ + mutex_lock(&lcd_mutex); + while (dmac_ch_running(&lcd_dma_ch)) + yield(); + + int xe = (x + width) - 1; /* max horiz */ + int ye = (y + height) - 1; /* max vert */ + + if (lcd_type & 2) { + s5l_lcd_write_reg(R_HORIZ_ADDR_START_POS, x); + s5l_lcd_write_reg(R_HORIZ_ADDR_END_POS, xe); + s5l_lcd_write_reg(R_VERT_ADDR_START_POS, y); + s5l_lcd_write_reg(R_VERT_ADDR_END_POS, ye); + + s5l_lcd_write_reg(R_HORIZ_GRAM_ADDR_SET, x); + s5l_lcd_write_reg(R_VERT_GRAM_ADDR_SET, y); + + s5l_lcd_write_cmd(R_WRITE_DATA_TO_GRAM); + } else { + s5l_lcd_write_cmd(R_COLUMN_ADDR_SET); + s5l_lcd_write_data(x >> 8); + s5l_lcd_write_data(x & 0xff); + s5l_lcd_write_data(xe >> 8); + s5l_lcd_write_data(xe & 0xff); + + s5l_lcd_write_cmd(R_ROW_ADDR_SET); + s5l_lcd_write_data(y >> 8); + s5l_lcd_write_data(y & 0xff); + s5l_lcd_write_data(ye >> 8); + s5l_lcd_write_data(ye & 0xff); + + s5l_lcd_write_cmd(R_MEMORY_WRITE); + } +} + +static void displaylcd_dma(int pixels) ICODE_ATTR; +static void displaylcd_dma(int pixels) +{ + commit_dcache(); + dmac_ch_queue(&lcd_dma_ch, lcd_dblbuf, + (void*)S5L8702_DADDR_PERI_LCD_WR, pixels*2, NULL); + mutex_unlock(&lcd_mutex); +} + +/* Update a fraction of the display. */ +void lcd_update_rect(int, int, int, int) ICODE_ATTR; +void lcd_update_rect(int x, int y, int width, int height) +{ + int pixels = width * height; + fb_data* p = FBADDR(x,y); + uint16_t* out = lcd_dblbuf[0]; + +#ifdef HAVE_LCD_SLEEP + if (!lcd_active()) return; +#endif + + displaylcd_setup(x, y, width, height); + + /* Copy display bitmap to hardware */ + if (LCD_WIDTH == width) { + /* Write all lines at once */ + memcpy(out, p, pixels * 2); + } else { + do { + /* Write a single line */ + memcpy(out, p, width * 2); + p += LCD_WIDTH; + out += width; + } while (--height); + } + + displaylcd_dma(pixels); +} + +/* Line write helper function for lcd_yuv_blit. Writes two lines of yuv420. */ +extern void lcd_write_yuv420_lines(unsigned char const * const src[3], + uint16_t* outbuf, + int width, + int stride); + +/* Blit a YUV bitmap directly to the LCD */ +void lcd_blit_yuv(unsigned char * const src[3], + int src_x, int src_y, int stride, + int x, int y, int width, int height) ICODE_ATTR; +void lcd_blit_yuv(unsigned char * const src[3], + int src_x, int src_y, int stride, + int x, int y, int width, int height) +{ + unsigned int z; + unsigned char const * yuv_src[3]; + +#ifdef HAVE_LCD_SLEEP + if (!lcd_active()) return; +#endif + + width = (width + 1) & ~1; /* ensure width is even */ + + int pixels = width * height; + uint16_t* out = lcd_dblbuf[0]; + + z = stride * src_y; + yuv_src[0] = src[0] + z + src_x; + yuv_src[1] = src[1] + (z >> 2) + (src_x >> 1); + yuv_src[2] = src[2] + (yuv_src[1] - src[1]); + + displaylcd_setup(x, y, width, height); + + height >>= 1; + + do { + lcd_write_yuv420_lines(yuv_src, out, width, stride); + yuv_src[0] += stride << 1; + yuv_src[1] += stride >> 1; /* Skip down one chroma line */ + yuv_src[2] += stride >> 1; + out += width << 1; + } while (--height); + + displaylcd_dma(pixels); +} diff --git a/firmware/target/arm/s5l8702/ipod6g/lcd-asm-6g.S b/firmware/target/arm/s5l8702/ipod6g/lcd-asm-6g.S new file mode 100644 index 0000000000..1ed7c4e189 --- /dev/null +++ b/firmware/target/arm/s5l8702/ipod6g/lcd-asm-6g.S @@ -0,0 +1,1013 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: lcd-as-video.S 26756 2010-06-11 04:41:36Z funman $ + * + * Copyright (C) 2010 by Andree Buschmann + * + * 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. + * + ****************************************************************************/ + +/* Version history: + * + * SVN: + * - initial SVN version. + * + * ARMv4: + * - use all available registers to calculate four pixels within each + * loop iteration. + * - avoid LDR interlocks. + * + * ARMv5TE: + * - use ARMv5TE+ 1-cycle multiply-accumulate instructions. + * + * ARMv5TE_WST: + * - use data tables (256 bytes) for RBG565 saturation. + * + * All versions are based on current SVN algorithm (round->scale->add) + * using the same coefficients, so output results are identical. + * + * TODO?: SVN coefficients are a very nice approximation for operations + * with shift+add instructions. When 16x16+32 MLA instructions are used, + * NBR and COEF_N could probably be adjusted to slighly increase accuracy. + */ +#define VERSION_SVN 0 +#define VERSION_ARMV4 1 +#define VERSION_ARMV5TE 2 +#define VERSION_ARMV5TE_WST 3 + +#define YUV2RGB_VERSION VERSION_ARMV5TE_WST + + +#define ASM +#include "config.h" +#include "cpu.h" + +#if (YUV2RGB_VERSION == VERSION_SVN) + .section .icode, "ax", %progbits + + +/**************************************************************************** + * extern void lcd_write_yuv420_lines(unsigned char const * const src[3], + * uint16_t* out, + * int width, + * int stride); + * + * Conversion from Motion JPEG and MPEG Y'PbPr to RGB is: + * |R| |1.164 0.000 1.596| |Y' - 16| + * |G| = |1.164 -0.391 -0.813| |Pb - 128| + * |B| |1.164 2.018 0.000| |Pr - 128| + * + * Scaled, normalized, rounded and tweaked to yield RGB 565: + * |R| |74 0 101| |Y' - 16| >> 9 + * |G| = |74 -24 -51| |Cb - 128| >> 8 + * |B| |74 128 0| |Cr - 128| >> 9 + * + * Converts two lines from YUV to RGB565 and writes to LCD at once. First loop + * loads Cb/Cr, calculates the chroma offset and saves them to buffer. Within + * the second loop these chroma offset are reloaded from buffer. Within each + * loop two pixels are calculated and written to LCD. + */ + .align 2 + .global lcd_write_yuv420_lines + .type lcd_write_yuv420_lines, %function +lcd_write_yuv420_lines: + /* r0 = src = yuv_src */ + /* r1 = dst = out */ + /* r2 = width */ + /* r3 = stride */ + stmfd sp!, { r4-r10, lr } /* save non-scratch */ + ldmia r0, { r9, r10, r12 } /* r9 = yuv_src[0] = Y'_p */ + /* r10 = yuv_src[1] = Cb_p */ + /* r12 = yuv_src[2] = Cr_p */ + add r3, r9, r3 /* r3 = &ysrc[stride] */ + add r4, r2, r2, asr #1 /* chroma buffer lenght = width/2 *3 */ + mov r4, r4, asl #2 /* use words for str/ldm possibility */ + add r4, r4, #15 /* plus room for 3 additional words, */ + bic r4, r4, #3 /* rounded up to multiples of 4 byte */ + sub sp, sp, r4 /* and allocate on stack */ + stmia sp, {r2-r4} /* width, &ysrc[stride], stack_alloc */ + + mov r7, r2 /* r7 = loop count */ + add r8, sp, #12 /* chroma buffer */ + mov lr, r1 /* RGB565 data destination buffer */ + + /* 1st loop start */ +10: /* loop start */ + + ldrb r0, [r10], #1 /* r0 = *usrc++ = *Cb_p++ */ + ldrb r1, [r12], #1 /* r1 = *vsrc++ = *Cr_p++ */ + + sub r0, r0, #128 /* r0 = Cb-128 */ + sub r1, r1, #128 /* r1 = Cr-128 */ + + add r2, r1, r1, asl #1 /* r2 = Cr*51 + Cb*24 */ + add r2, r2, r2, asl #4 + add r2, r2, r0, asl #3 + add r2, r2, r0, asl #4 + + add r4, r1, r1, asl #2 /* r1 = Cr*101 */ + add r4, r4, r1, asl #5 + add r1, r4, r1, asl #6 + + add r1, r1, #256 /* r1 = rv = (r1 + 256) >> 9 */ + mov r1, r1, asr #9 + rsb r2, r2, #128 /* r2 = guv = (-r2 + 128) >> 8 */ + mov r2, r2, asr #8 + add r0, r0, #2 /* r0 = bu = (Cb*128 + 256) >> 9 */ + mov r0, r0, asr #2 + stmia r8!, {r0-r2} /* store r0, r1 and r2 to chroma buffer */ + + /* 1st loop, first pixel */ + ldrb r5, [r9], #1 /* r5 = *ysrc++ = *Y'_p++ */ + sub r5, r5, #16 /* r5 = (Y'-16) * 74 */ + add r3, r5, r5, asl #2 + add r5, r3, r5, asl #5 + + add r6, r1, r5, asr #8 /* r6 = r = (Y >> 9) + rv */ + add r3, r2, r5, asr #7 /* r3 = g = (Y >> 8) + guv */ + add r4, r0, r5, asr #8 /* r4 = b = (Y >> 9) + bu */ + + orr r5, r6, r4 /* check if clamping is needed... */ + orr r5, r5, r3, asr #1 /* ...at all */ + cmp r5, #31 + bls 15f /* -> no clamp */ + cmp r6, #31 /* clamp r */ + mvnhi r6, r6, asr #31 + andhi r6, r6, #31 + cmp r3, #63 /* clamp g */ + mvnhi r3, r3, asr #31 + andhi r3, r3, #63 + cmp r4, #31 /* clamp b */ + mvnhi r4, r4, asr #31 + andhi r4, r4, #31 +15: /* no clamp */ + + /* calculate pixel_1 and save to r4 for later pixel packing */ + orr r4, r4, r3, lsl #5 /* pixel_1 = r<<11 | g<<5 | b */ + orr r4, r4, r6, lsl #11 /* r4 = pixel_1 */ + + /* 1st loop, second pixel */ + ldrb r5, [r9], #1 /* r5 = *ysrc++ = *Y'_p++ */ + sub r5, r5, #16 /* r5 = (Y'-16) * 74 */ + add r3, r5, r5, asl #2 + add r5, r3, r5, asl #5 + + add r6, r1, r5, asr #8 /* r6 = r = (Y >> 9) + rv */ + add r3, r2, r5, asr #7 /* r3 = g = (Y >> 8) + guv */ + add r5, r0, r5, asr #8 /* r5 = b = (Y >> 9) + bu */ + + orr r0, r6, r5 /* check if clamping is needed... */ + orr r0, r0, r3, asr #1 /* ...at all */ + cmp r0, #31 + bls 15f /* -> no clamp */ + cmp r6, #31 /* clamp r */ + mvnhi r6, r6, asr #31 + andhi r6, r6, #31 + cmp r3, #63 /* clamp g */ + mvnhi r3, r3, asr #31 + andhi r3, r3, #63 + cmp r5, #31 /* clamp b */ + mvnhi r5, r5, asr #31 + andhi r5, r5, #31 +15: /* no clamp */ + + /* calculate pixel_2 and pack with pixel_1 before writing */ + orr r5, r5, r3, lsl #5 /* pixel_2 = r<<11 | g<<5 | b */ + orr r5, r5, r6, lsl #11 /* r5 = pixel_2 */ + orr r4, r4, r5, lsl #16 + str r4, [lr], #4 /* write pixel_1 and pixel_2 */ + + subs r7, r7, #2 /* check for loop end */ + bgt 10b /* back to beginning */ + /* 1st loop end */ + + /* Reload several registers for pointer rewinding for next loop */ + add r8, sp, #12 /* chroma buffer */ + ldmia sp, {r7, r9} /* r7 = loop count */ + /* r9 = &ysrc[stride] */ + + /* 2nd loop start */ +20: /* loop start */ + /* restore r0 (bu), r1 (rv) and r2 (guv) from chroma buffer */ + ldmia r8!, {r0-r2} + + /* 2nd loop, first pixel */ + ldrb r5, [r9], #1 /* r5 = *ysrc++ = *Y'_p++ */ + sub r5, r5, #16 /* r5 = (Y'-16) * 74 */ + add r3, r5, r5, asl #2 + add r5, r3, r5, asl #5 + + add r6, r1, r5, asr #8 /* r6 = r = (Y >> 9) + rv */ + add r3, r2, r5, asr #7 /* r3 = g = (Y >> 8) + guv */ + add r4, r0, r5, asr #8 /* r4 = b = (Y >> 9) + bu */ + + orr r5, r6, r4 /* check if clamping is needed... */ + orr r5, r5, r3, asr #1 /* ...at all */ + cmp r5, #31 + bls 15f /* -> no clamp */ + cmp r6, #31 /* clamp r */ + mvnhi r6, r6, asr #31 + andhi r6, r6, #31 + cmp r3, #63 /* clamp g */ + mvnhi r3, r3, asr #31 + andhi r3, r3, #63 + cmp r4, #31 /* clamp b */ + mvnhi r4, r4, asr #31 + andhi r4, r4, #31 +15: /* no clamp */ + /* calculate pixel_1 and save to r4 for later pixel packing */ + orr r4, r4, r3, lsl #5 /* pixel_1 = r<<11 | g<<5 | b */ + orr r4, r4, r6, lsl #11 /* r4 = pixel_1 */ + + /* 2nd loop, second pixel */ + ldrb r5, [r9], #1 /* r5 = *ysrc++ = *Y'_p++ */ + sub r5, r5, #16 /* r5 = (Y'-16) * 74 */ + add r3, r5, r5, asl #2 + add r5, r3, r5, asl #5 + + add r6, r1, r5, asr #8 /* r6 = r = (Y >> 9) + rv */ + add r3, r2, r5, asr #7 /* r3 = g = (Y >> 8) + guv */ + add r5, r0, r5, asr #8 /* r5 = b = (Y >> 9) + bu */ + + orr r0, r6, r5 /* check if clamping is needed... */ + orr r0, r0, r3, asr #1 /* ...at all */ + cmp r0, #31 + bls 15f /* -> no clamp */ + cmp r6, #31 /* clamp r */ + mvnhi r6, r6, asr #31 + andhi r6, r6, #31 + cmp r3, #63 /* clamp g */ + mvnhi r3, r3, asr #31 + andhi r3, r3, #63 + cmp r5, #31 /* clamp b */ + mvnhi r5, r5, asr #31 + andhi r5, r5, #31 +15: /* no clamp */ + + /* calculate pixel_2 and pack with pixel_1 before writing */ + orr r5, r5, r3, lsl #5 /* pixel_2 = r<<11 | g<<5 | b */ + orr r5, r5, r6, lsl #11 /* r5 = pixel_2 */ + orr r4, r4, r5, lsl #16 + str r4, [lr], #4 /* write pixel_1 and pixel_2 */ + + subs r7, r7, #2 /* check for loop end */ + bgt 20b /* back to beginning */ + /* 2nd loop end */ + + ldr r3, [sp, #8] + add sp, sp, r3 /* deallocate buffer */ + ldmpc regs=r4-r10 /* restore registers */ + + .ltorg + .size lcd_write_yuv420_lines, .-lcd_write_yuv420_lines + + +#elif (YUV2RGB_VERSION == VERSION_ARMV4) +/**************************************************************************** + * extern void lcd_write_yuv420_lines(unsigned char const * const src[3], + * uint16_t* out, + * int width, + * int stride); + * + * Conversion from Motion JPEG and MPEG Y'PbPr to RGB is: + * |R| |1.164 0.000 1.596| |Y' - 16| + * |G| = |1.164 -0.391 -0.813| |Pb - 128| + * |B| |1.164 2.018 0.000| |Pr - 128| + * + * Scaled, normalized, rounded and tweaked to yield RGB 565: + * |R| |74 0 101| |Y' - 16| >> 9 + * |G| = |74 -24 -51| |Cb - 128| >> 8 + * |B| |74 128 0| |Cr - 128| >> 9 + * + * Converts two lines from YUV420 to RGB565, within each iteration four + * pixels (2 per line) are calculated and written to destination buffer. + */ + .section .icode, "ax", %progbits + + .align 2 + .global lcd_write_yuv420_lines + .type lcd_write_yuv420_lines, %function + +lcd_write_yuv420_lines: + /* r0 = src = yuv_src */ + /* r1 = dst = out */ + /* r2 = width */ + /* r3 = stride */ + stmfd sp!, {r4-r11,lr} /* save non-scratch */ + ldmia r0, {r10-r12} /* r10 = yuv_src[0] = Y'_p */ + /* r11 = yuv_src[1] = Cb_p */ + /* r12 = yuv_src[2] = Cr_p */ + mov r9, r2, lsl #1 /* r9 = 2*width (loop count) */ + str r9, [sp, #-4]! /* [--sp] = 2*width (constant) */ + add r8, r10, r3 /* r8 = Y'_p + stride = Y'stride_p */ + mov lr, r1 /* RGB565 data destination buffer */ + +10: /* loop start */ + ldrb r0, [r11], #1 /* r0 = *Cb_p++ */ + ldrb r1, [r12], #1 /* r1 = *Cr_p++ */ + ldrb r3, [r8], #1 /* r3 = Y'3 */ + ldrb r4, [r8], #1 /* r4 = Y'4 */ + + sub r0, r0, #128 /* r0 = Cb-128 */ + sub r1, r1, #128 /* r1 = Cr-128 */ + + add r2, r1, r1, asl #1 /* r2 = Cr*51 + Cb*24 */ + add r2, r2, r2, asl #4 + add r2, r2, r0, asl #3 + add r2, r2, r0, asl #4 + + add r5, r1, r1, asl #2 /* r1 = Cr*101 */ + add r5, r5, r1, asl #5 + add r1, r5, r1, asl #6 + + add r1, r1, #256 /* r1 = rv = (r1 + 256) >> 9 */ + mov r1, r1, asr #9 + rsb r2, r2, #128 /* r2 = guv = (-r2 + 128) >> 8 */ + mov r2, r2, asr #8 + add r0, r0, #2 /* r0 = bu = (Cb*128 + 256) >> 9 */ + mov r0, r0, asr #2 + + /* pixel_3 */ + sub r3, r3, #16 /* r3 = (Y'-16) * (74/2) */ + add r7, r3, r3, asl #2 + add r3, r7, r3, asl #5 + + add r6, r1, r3, asr #8 /* r6 = r = (Y >> 9) + rv */ + add r7, r2, r3, asr #7 /* r7 = g = (Y >> 8) + guv */ + add r5, r0, r3, asr #8 /* r5 = b = (Y >> 9) + bu */ + + orr r3, r6, r5 /* check if clamping is needed... */ + orr r3, r3, r7, asr #1 /* ...at all */ + cmp r3, #31 + bls 15f /* no clamp */ + cmp r6, #31 /* clamp r */ + mvnhi r6, r6, asr #31 + andhi r6, r6, #31 + cmp r7, #63 /* clamp g */ + mvnhi r7, r7, asr #31 + andhi r7, r7, #63 + cmp r5, #31 /* clamp b */ + mvnhi r5, r5, asr #31 + andhi r5, r5, #31 +15: /* no clamp */ + + /* calculate pixel_3 and save to r5 for later pixel packing */ + orr r5, r5, r7, lsl #5 /* pixel_3 = r<<11 | g<<5 | b */ + orr r5, r5, r6, lsl #11 /* r5 = pixel_3 */ + + /* pixel_4 */ + sub r4, r4, #16 /* r4 = (Y'-16) * (74/2) */ + add r7, r4, r4, asl #2 + add r4, r7, r4, asl #5 + + add r6, r1, r4, asr #8 /* r6 = r = (Y >> 9) + rv */ + add r7, r2, r4, asr #7 /* r7 = g = (Y >> 8) + guv */ + add r4, r0, r4, asr #8 /* r4 = b = (Y >> 9) + bu */ + + orr r3, r6, r4 /* check if clamping is needed... */ + orr r3, r3, r7, asr #1 /* ...at all */ + cmp r3, #31 + bls 15f /* no clamp */ + cmp r6, #31 /* clamp r */ + mvnhi r6, r6, asr #31 + andhi r6, r6, #31 + cmp r7, #63 /* clamp g */ + mvnhi r7, r7, asr #31 + andhi r7, r7, #63 + cmp r4, #31 /* clamp b */ + mvnhi r4, r4, asr #31 + andhi r4, r4, #31 +15: /* no clamp */ + + /* calculate pixel_4 and pack with pixel_3 before writing */ + orr r4, r4, r7, lsl #5 /* pixel_4 = r<<11 | g<<5 | b */ + orr r4, r4, r6, lsl #11 /* r4 = pixel_4 */ + orr r5, r5, r4, lsl #16 /* r5 = pixel_4<<16 | pixel_3 */ + + ldr r7, [sp] /* r7 = 2*width */ + ldrb r3, [r10], #1 /* r3 = Y'1 */ + ldrb r4, [r10], #1 /* r4 = Y'2 */ + + str r5, [lr, r7] /* write pixel_3 and pixel_4 */ + + /* pixel_1 */ + sub r3, r3, #16 /* r3 = (Y'-16) * (74/2) */ + add r7, r3, r3, asl #2 + add r3, r7, r3, asl #5 + + add r6, r1, r3, asr #8 /* r6 = r = (Y >> 9) + rv */ + add r7, r2, r3, asr #7 /* r7 = g = (Y >> 8) + guv */ + add r5, r0, r3, asr #8 /* r5 = b = (Y >> 9) + bu */ + + orr r3, r6, r5 /* check if clamping is needed... */ + orr r3, r3, r7, asr #1 /* ...at all */ + cmp r3, #31 + bls 15f /* no clamp */ + cmp r6, #31 /* clamp r */ + mvnhi r6, r6, asr #31 + andhi r6, r6, #31 + cmp r7, #63 /* clamp g */ + mvnhi r7, r7, asr #31 + andhi r7, r7, #63 + cmp r5, #31 /* clamp b */ + mvnhi r5, r5, asr #31 + andhi r5, r5, #31 +15: /* no clamp */ + + /* calculate pixel_1 and save to r5 for later pixel packing */ + orr r5, r5, r7, lsl #5 /* pixel_1 = r<<11 | g<<5 | b */ + orr r5, r5, r6, lsl #11 /* r5 = pixel_1 */ + + /* pixel_2 */ + sub r4, r4, #16 /* r4 = (Y'-16) * (74/2) */ + add r7, r4, r4, asl #2 + add r4, r7, r4, asl #5 + + add r6, r1, r4, asr #8 /* r6 = r = (Y >> 9) + rv */ + add r7, r2, r4, asr #7 /* r7 = g = (Y >> 8) + guv */ + add r4, r0, r4, asr #8 /* r4 = b = (Y >> 9) + bu */ + + orr r3, r6, r4 /* check if clamping is needed... */ + orr r3, r3, r7, asr #1 /* ...at all */ + cmp r3, #31 + bls 15f /* no clamp */ + cmp r6, #31 /* clamp r */ + mvnhi r6, r6, asr #31 + andhi r6, r6, #31 + cmp r7, #63 /* clamp g */ + mvnhi r7, r7, asr #31 + andhi r7, r7, #63 + cmp r4, #31 /* clamp b */ + mvnhi r4, r4, asr #31 + andhi r4, r4, #31 +15: /* no clamp */ + + /* calculate pixel_2 and pack with pixel_1 before writing */ + orr r4, r4, r7, lsl #5 /* pixel_2 = r<<11 | g<<5 | b */ + orr r4, r4, r6, lsl #11 /* r4 = pixel_2 */ + orr r5, r5, r4, lsl #16 /* r5 = pixel_2<<16 | pixel_1 */ + + str r5, [lr], #4 /* write pixel_1 and pixel_2 */ + + subs r9, r9, #4 /* check for loop end */ + bgt 10b /* back to beginning */ + + /* loop end */ + add sp, sp, #4 /* deallocate stack */ + ldmpc regs=r4-r11 /* restore registers */ + + .ltorg + .size lcd_write_yuv420_lines, .-lcd_write_yuv420_lines + + +#elif (YUV2RGB_VERSION == VERSION_ARMV5TE) +/**************************************************************************** + * How do I encode Y'CBCR components from R'G'B' in [0, +1]? (see ColorFAQ) + * |R| |0.00456621 0 0.00625893| |Y' - 16| + * |G| = |0.00456621 -0.00153632 -0.00318811| |Pb - 128| + * |B| |0.00456621 0.00791071 0 | |Pr - 128| + * + * Scaled, normalized, rounded and tweaked to yield RGB 565: + * |R| |74 0 101| |Y' - 16| >> 9 + * |G| = |74 -24 -51| |Cb - 128| >> 8 + * |B| |74 128 0| |Cr - 128| >> 9 + */ +#define NBR 14 /* 14-bit resolution (SVN) */ +#define COEF_C0 74 +#define COEF_C1 101 +#define COEF_C2 -24 +#define COEF_C3 -51 +#define COEF_C4 128 +#define C4_IS_POW2 + +/* constant for rounding a NBR number before down-scaling it to RS bits */ +#define ROUND(RS) (1 << (NBR - RS - 1)) + +/* packed 16-bit coefficients */ +#define COEF_C4_C1 ((COEF_C4 << 16) | (COEF_C1 & 0xffff)) +#define COEF_2C3_2C2 ((COEF_C3 << 17) | ((COEF_C2 << 1) & 0xffff)) +/* 32-bit MLA constants */ +#define CONST_MLA_Y (-16 * COEF_C0) + +/**************************************************************************** + * extern void lcd_write_yuv420_lines(unsigned char const * const src[3], + * uint16_t* out, + * int width, + * int stride); + * + * Converts two lines from YUV420 to RGB565, within each iteration four + * pixels (2 per line) are calculated and written to destination buffer. + * + * - use ARMv5TE+ 1-cycle multiply+accumulator instructions. + */ + .section .icode, "ax", %progbits + + .align 2 + .global lcd_write_yuv420_lines + .type lcd_write_yuv420_lines, %function + +lcd_write_yuv420_lines: + @ r0 = src = yuv_src + @ r1 = out = dst_p + @ r2 = width + @ r3 = stride + stmfd sp!, {r4-r11,lr} @ save non-scratch + ldmia r0, {r10-r12} @ r10 = yuv_src[0] = Y'_p + @ r11 = yuv_src[1] = Cb_p + @ r12 = yuv_src[2] = Cr_p + adr r0, const_data @ load constants + ldmia r0, {r5-r8} @ r5 = COEF_C4_C1 + @ r6 = COEF_2C3_2C2 + @ r7 = COEF_C0 + @ r8 = CONST_MLA_Y + sub r4, r12, r11 @ r4 = Cr_p-Cb_p + mov r9, r2, asl #1 @ r9 = 2*width + stmfd sp!, {r4-r6,r9} @ SP -> Cr_p-Cb_p + @ COEF_C4_C1 + @ COEF_2C3_2C2 + @ 2*width + add r12, r10, r3 @ r12 = Y'_p + stride = Y'stride_p + mov lr, r1 @ RGB565 data destination buffer + orr r9, r7, r2, lsl #15 @ loop_count = width/2; + @ r9 = loop_count<<16 | COEF_C0 + sub r9, r9, #0x10000 @ loop_count-- + +10: @ loop_start + + @ register usage: + @ r8 = CONST_MLA_Y + @ r9 = loop count<<16 | COEF_C0 + @ r10 = Y'_p + @ r11 = Cb_p + @ r12 = Y'stride_p + @ lr = dst_p + @ free: r0-r7 + + ldmia sp, {r2-r4} @ r2 = Cr_p-Cb_p + @ r3 = COEF_C4_C1 + @ r4 = COEF_2C3_2C2 + mov r5, #ROUND(5) @ r5 = round constant + + ldrb r6, [r12], #1 @ r6 = Y'3 + ldrb r7, [r12], #1 @ r7 = Y'4 + + ldrb r1, [r11, r2] @ r1 = Cr = *Cr_p++ + ldrb r0, [r11], #1 @ r0 = Cb = *Cb_p++ + + /* calculate Y3 and Y4 */ + smlabb r6, r6, r9, r8 @ r6 = Y3 = C0*Y'3 - C0*16 + smlabb r7, r7, r9, r8 @ r7 = Y4 = C0*Y'4 - C0*16 + + /* calculate rv, guv, bu */ + sub r1, r1, #128 @ r1 = Cr" = Cr-128 + sub r0, r0, #128 @ r0 = Cb" = Cb-128 + + smlabt r2, r1, r4, r5 @ r2 = guv" = Cr"*(2*C2) + + smlabb r2, r0, r4, r2 @ Cb"*(2*C3) + round + smlabb r1, r1, r3, r5 @ r1 = rv" = Cr"*C1 + round + #ifdef C4_IS_POW2 + add r0, r5, r0, asl #NBR-7 @ r0 = bu" = Cb"*C4 + round + #else + smlabt r0, r0, r3, r5 @ r0 = bu" = Cb"*C4 + round + #endif + + /* scale rv",guv",bu" */ + mov r2, r2, asr #NBR-5 @ r2 = guv = guv" >> scale + mov r1, r1, asr #NBR-5 @ r1 = rv = rv" >> scale + mov r0, r0, asr #NBR-5 @ r0 = bu = bu" >> scale + + @ register usage: + @ r8-r12,lr: pointers, counters + @ r0,r1,r2 = bu,rv,guv (rounded and scaled to RGB565) + @ r6,r7 = Y'3,Y'4 + @ free: r3-r5 + + /* pixel_3 */ + add r5, r1, r6, asr #NBR-5 @ r5 = r = (Y3 >> scale) + rv + add r4, r2, r6, asr #NBR-6 @ r4 = g = (Y3 >> scale) + guv + add r3, r0, r6, asr #NBR-5 @ r3 = b = (Y3 >> scale) + bu + + orr r6, r5, r3 @ check if clamping is needed... + orr r6, r6, r4, asr #1 @ ...at all + cmp r6, #31 + bls 15f @ no clamp + cmp r5, #31 @ clamp r + mvnhi r5, r5, asr #31 + andhi r5, r5, #31 + cmp r4, #63 @ clamp g + mvnhi r4, r4, asr #31 + andhi r4, r4, #63 + cmp r3, #31 @ clamp b + mvnhi r3, r3, asr #31 + andhi r3, r3, #31 +15: @ no clamp + + /* calculate pixel_3 and save to r3 for later pixel packing */ + orr r3, r3, r4, lsl #5 @ r3 = pixel_3 = r<<11 | g<<5 | b + orr r3, r3, r5, lsl #11 + + /* pixel_4 */ + add r5, r1, r7, asr #NBR-5 @ r5 = r = (Y4 >> scale) + rv + add r4, r2, r7, asr #NBR-6 @ r4 = g = (Y4 >> scale) + guv + add r7, r0, r7, asr #NBR-5 @ r7 = b = (Y4 >> scale) + bu + + orr r6, r5, r7 @ check if clamping is needed... + orr r6, r6, r4, asr #1 @ ...at all + cmp r6, #31 + bls 15f @ no clamp + cmp r5, #31 @ clamp r + mvnhi r5, r5, asr #31 + andhi r5, r5, #31 + cmp r4, #63 @ clamp g + mvnhi r4, r4, asr #31 + andhi r4, r4, #63 + cmp r7, #31 @ clamp b + mvnhi r7, r7, asr #31 + andhi r7, r7, #31 +15: @ no clamp + + /* calculate pixel_4 and pack with pixel_3 before writing */ + orr r7, r7, r4, lsl #5 @ r7 = pixel_4 = r<<11 | g<<5 | b + orr r7, r7, r5, lsl #11 + orr r3, r3, r7, lsl #16 @ r3 = pixel_4<<16 | pixel_3 + + /* avoid interlocks when writing pixel_3 and pixel_4 */ + ldr r5, [sp, #12] @ r5 = 2*width + + ldrb r6, [r10], #1 @ r6 = Y'1 + ldrb r7, [r10], #1 @ r7 = Y'2 + + /* write pixel_3 and pixel_4 */ + str r3, [lr, r5] @ [dst_p + 2*width] = r3 + + @ register usage: + @ r8-r12,lr: pointers, counters + @ r0,r1,r2 = bu,rv,guv (rounded and scaled to RGB565) + @ r6,r7 = Y'1,Y'2 + @ free: r3-r5 + + /* calculate Y1 and Y2 */ + smlabb r6, r6, r9, r8 @ r6 = Y1 = C0*Y'1 - C0*16 + smlabb r7, r7, r9, r8 @ r7 = Y2 = C0*Y'2 - C0*16 + + /* pixel_1 */ + add r5, r1, r6, asr #NBR-5 @ r5 = r = (Y1 >> scale) + rv + add r4, r2, r6, asr #NBR-6 @ r4 = g = (Y1 >> scale) + guv + add r3, r0, r6, asr #NBR-5 @ r3 = b = (Y1 >> scale) + bu + + orr r6, r5, r3 @ check if clamping is needed... + orr r6, r6, r4, asr #1 @ ...at all + cmp r6, #31 + bls 15f @ no clamp + cmp r5, #31 @ clamp r + mvnhi r5, r5, asr #31 + andhi r5, r5, #31 + cmp r4, #63 @ clamp g + mvnhi r4, r4, asr #31 + andhi r4, r4, #63 + cmp r3, #31 @ clamp b + mvnhi r3, r3, asr #31 + andhi r3, r3, #31 +15: @ no clamp + + /* calculate pixel_1 and save to r3 for later pixel packing */ + orr r3, r3, r4, lsl #5 @ r3 = pixel_1 = r<<11 | g<<5 | b + orr r3, r3, r5, lsl #11 + + /* pixel_2 */ + add r5, r1, r7, asr #NBR-5 @ r5 = r = (Y2 >> scale) + rv + add r4, r2, r7, asr #NBR-6 @ r4 = g = (Y2 >> scale) + guv + add r7, r0, r7, asr #NBR-5 @ r7 = b = (Y2 >> scale) + bu + + orr r6, r5, r7 @ check if clamping is needed... + orr r6, r6, r4, asr #1 @ ...at all + cmp r6, #31 + bls 15f @ no clamp + cmp r5, #31 @ clamp r + mvnhi r5, r5, asr #31 + andhi r5, r5, #31 + cmp r4, #63 @ clamp g + mvnhi r4, r4, asr #31 + andhi r4, r4, #63 + cmp r7, #31 @ clamp b + mvnhi r7, r7, asr #31 + andhi r7, r7, #31 +15: @ no clamp + + /* calculate pixel_2 and pack with pixel_1 before writing */ + orr r7, r7, r4, lsl #5 @ r7 = pixel_2 = r<<11 | g<<5 | b + orr r7, r7, r5, lsl #11 + orr r3, r3, r7, lsl #16 @ r3 = pixel_2 << 16 | pixel_1 + + str r3, [lr], #4 @ write pixel_1 and pixel_2 + + /* check for loop end */ + subs r9, r9, #0x10000 @ loop_count-- + bge 10b @ back to beginning + + /* bye */ + add sp, sp, #16 + ldmpc regs=r4-r11 @ restore registers + + .ltorg + .size lcd_write_yuv420_lines, .-lcd_write_yuv420_lines + +/* data */ + .align 2 +const_data: + .word COEF_C4_C1 + .word COEF_2C3_2C2 + .word COEF_C0 + .word CONST_MLA_Y + + .size const_data, .-const_data + + +#else /* YUV2RGB_VERSION == VERSION_ARMV5TE_WST */ +/**************************************************************************** + * How do I encode Y'CBCR components from R'G'B' in [0, +1]? (see ColorFAQ) + * |R| |0.00456621 0 0.00625893| |Y' - 16| + * |G| = |0.00456621 -0.00153632 -0.00318811| |Pb - 128| + * |B| |0.00456621 0.00791071 0 | |Pr - 128| + * + * Scaled, normalized, rounded and tweaked to yield RGB 565: + * |R| |74 0 101| |Y' - 16| >> 9 + * |G| = |74 -24 -51| |Cb - 128| >> 8 + * |B| |74 128 0| |Cr - 128| >> 9 + */ +#define NBR 14 /* 14-bit resolution (SVN) */ +#define COEF_C0 74 +#define COEF_C1 101 +#define COEF_C2 -24 +#define COEF_C3 -51 +#define COEF_C4 128 +#define C4_IS_POW2 + +/* packed 16-bit coefficients */ +#define COEF_C4_C1 ((COEF_C4 << 16) | (COEF_C1 & 0xffff)) +#define COEF_C3_C2 ((COEF_C3 << 16) | (COEF_C2 & 0xffff)) + +/* constant for rounding an NBR number before down-scaling it to RS bits */ +#define ROUND(RS) (1 << (NBR - RS - 1)) + +/* 32-bit MLA constants */ +#define CONST_MLA_Y (-16 * COEF_C0) +#define CONST_MLA_RV ((-128 * COEF_C1) + ROUND(5)) +#define CONST_MLA_BU ((-128 * COEF_C4) + ROUND(5)) +/* trick to save the register needed for table_sat6 reference: + add table_sat6-table_sat5 offset (conveniently scaled) to guv MLA */ +#define CONST_MLA_GUV (-128 * (COEF_C2 + COEF_C3) + ROUND(6) + \ + ((table_sat6 - table_sat5) << (NBR - 6))) + +/**************************************************************************** + * extern void lcd_write_yuv420_lines(unsigned char const * const src[3], + * uint16_t* out, + * int width, + * int stride); + * + * Converts two lines from YUV420 to RGB565, within each iteration four + * pixels (2 per line) are calculated and written to destination buffer. + * + * - use ARMv5TE+ 1-cycle multiply+accumulator instructions. + * - use data tables (256 bytes) for RBG565 saturation. + */ + .section .icode, "ax", %progbits + + .align 2 + .global lcd_write_yuv420_lines + .type lcd_write_yuv420_lines, %function + +lcd_write_yuv420_lines: + @ r0 = src = yuv_src + @ r1 = out = dst1_p + @ r2 = width + @ r3 = stride + stmfd sp!, {r4-r11,lr} @ save non-scratch + ldmia r0, {r10-r12} @ r10 = yuv_src[0] = Y'_p + @ r11 = yuv_src[1] = Cb_p + @ r12 = yuv_src[2] = Cr_p + /* prepare data and fill stack */ + adr r0, const_data @ load constants + ldmia r0, {r4-r9,lr} @ r4 = COEF_C0 + @ r5 = CONST_MLA_GUV + @ r6 = COEF_C3_C2 + @ r7 = CONST_MLA_BU + @ r8 = COEF_C4_C1 + @ r9 = CONST_MLA_RV + @ lr = table_sat5 + sub r0, r12, r11 @ r0 = Cr_p-Cb_p + #define STACK_SZ 28 + stmfd sp!, {r0,r5-r9,lr} @ SP -> Cr_p-Cb_p + @ CONST_MLA_GUV + @ COEF_C3_C2 + @ CONST_MLA_BU + @ COEF_C4_C1 + @ CONST_MLA_RV + @ table_sat5 + mov r8, r4, lsl #4 @ + rsb r8, #0 @ r8 = -16*COEF_C0 = CONST_MLA_Y + mov lr, r1 @ RGB565 data destination buffer + add r9, lr, r2, asl #1 @ r9 = out + 2*width = dst2_p + add r12, r3, r10 @ r12 = Y'_p + stride + orr r7, r4, r2, lsl #15 @ loop_count = width/2; + @ r7 = loop_count<<16 | COEF_C0 + sub r7, r7, #0x10000 @ loop_count-- + + /* align loop code to minimize occupied lines, execution + time per loop is optimized ~10% on ARM926EJ-S */ + .align CACHEALIGN_BITS +loop_start: + + @ register usage: + @ r7 = loop count<<16 | COEF_C0 + @ r8 = CONST_MLA_Y + @ r9 = dst2_p + @ r10 = Y'_p + @ r11 = Cb_p + @ r12 = Y'stride_p + @ lr = dst1_p + @ free: r0-r6 + + /* load constants from stack */ + ldmia sp, {r1-r3,r6} @ r1 = Cr_p-Cb_p + @ r2 = CONST_MLA_GUV + @ r3 = COEF_C3_C2 + @ r6 = CONST_MLA_BU + + /* read Cr", Cb" */ + ldrb r1, [r11, r1] @ r1 = Cr = *Cr_p++ + ldrb r0, [r11], #1 @ r0 = Cb = *Cb_p++ + + /* load more constants (avoids r1 interlock) */ + ldrd r4, [sp, #16] @ r4 = COEF_C4_C1 + @ r5 = CONST_MLA_RV + + /* calculate rv", guv", bu" */ + smlabt r2, r1, r3, r2 @ r2 = guv" = Cr*C2 + Cb*C3 + smlabb r2, r0, r3, r2 @ + CONST_MLA_GUV + smlabb r1, r1, r4, r5 @ r1 = rv" = Cr*C1 + CONST_MLA_RV + #ifdef C4_IS_POW2 + add r0, r6, r0, asl #NBR-7 @ r0 = bu" = Cb*C4 + CONST_MLA_BU + #else + smlabt r0, r0, r4, r6 @ r0 = bu" = Cb*C4 + CONST_MLA_BU + #endif + + ldr r4, [sp, #STACK_SZ-4] @ r4 = table_sat5 + + /* read Y'1 and Y'2 */ + ldrb r5, [r10], #1 @ r5 = Y'1 = *Y'_p++ + ldrb r6, [r10], #1 @ r6 = Y'2 = *Y'_p++ + + /* scale rv",guv",bu", adding sat5_p here saves instructions later */ + add r1, r4, r1, asr #NBR-5 @ r1 = rv' = sat5_p + rv">>scale + add r2, r4, r2, asr #NBR-6 @ r2 = guv' = sat5_p + guv">>scale + add r0, r4, r0, asr #NBR-5 @ r0 = bu' = sat5_p + bu">>scale + + @ register usage: + @ r7-r12,lr: pointers, counters, tables + @ r0,r1,r2 = (bu,rv,guv) rounded and RGB565 scaled + @ r5,r6 = Y'1,Y'2 + @ free: r3,r4 + + /* calculate Y1 and Y2 */ + smlabb r5, r5, r7, r8 @ r5 = Y1 = C0*Y'1 - 16*C0 + smlabb r6, r6, r7, r8 @ r6 = Y2 = C0*Y'2 - 16*C0 + + /* pixel_1 */ + ldrb r3, [r0, r5, asr #NBR-5] @ r3 = b = sat5[Y1>>scale + bu'] + ldrb r4, [r2, r5, asr #NBR-6] @ r4 = g = sat6[Y1>>scale + guv'] + ldrb r5, [r1, r5, asr #NBR-5] @ r5 = r = sat5[Y1>>scale + rv'] + + /* calculate pixel_1 */ + orr r3, r3, r4, lsl #5 @ r3 = pixel_1 = g<<5 | b + + /* pixel_2 (avoid r5 interlock) */ + ldrb r4, [r0, r6, asr #NBR-5] @ r4 = b = sat5[Y2>>scale + bu'] + + /* calculate pixel_1 and save to r3 for later pixel packing */ + orr r3, r3, r5, lsl #11 @ r3 = pixel_1 = r<<11 | g<<5 | b + + /* pixel_2 */ + ldrb r5, [r2, r6, asr #NBR-6] @ r5 = g = sat6[Y2>>scale + guv'] + ldrb r6, [r1, r6, asr #NBR-5] @ r6 = r = sat5[Y2>>scale + rv'] + + /* calculate pixel_2 and pack with pixel_1 before writing */ + orr r3, r3, r4, lsl #16 @ r3 = pixel_2<<16 | pixel_1 + orr r3, r3, r5, lsl #21 + orr r3, r3, r6, lsl #27 + + /* read Y'3 and Y'4 */ + ldrb r5, [r12], #1 @ r5 = Y'3 = *Y'stride_p++ + ldrb r6, [r12], #1 @ r6 = Y'4 = *Y'stride_p++ + + /* write pixel_1 and pixel_2 */ + str r3, [lr], #4 @ *dst2_p++ = r3 + + @ register usage: + @ r7-r12,lr: pointers, counters, tables + @ r0,r1,r2 = (bu,rv,guv) rounded and RGB565 scaled + @ r5,r6 = Y'3,Y'4 + @ free: r3,r4 + + /* calculate Y3 and Y4 */ + smlabb r5, r5, r7, r8 @ r5 = Y3 = C0*Y'3 - 16*C0 + smlabb r6, r6, r7, r8 @ r6 = Y4 = C0*Y'4 - 16*C0 + + /* pixel_3 */ + ldrb r3, [r0, r5, asr #NBR-5] @ r3 = b = sat5[Y3>>scale + bu'] + ldrb r4, [r2, r5, asr #NBR-6] @ r4 = g = sat6[Y3>>scale + guv'] + ldrb r5, [r1, r5, asr #NBR-5] @ r5 = r = sat5[Y3>>scale + rv'] + + /* calculate pixel_3 */ + orr r3, r3, r4, lsl #5 @ r3 = pixel_3 = g<<5 | b + + /* pixel_4 (avoid r5 interlock) */ + ldrb r4, [r0, r6, asr #NBR-5] @ r4 = b = sat5[Y4>>scale + bu'] + + /* calculate pixel_3 and save to r3 for later pixel packing */ + orr r3, r3, r5, lsl #11 @ r3 = pixel_3 = r<<11 | g<<5 | b + + /* pixel_4 */ + ldrb r5, [r2, r6, asr #NBR-6] @ r5 = g = sat6[Y4>>scale + guv'] + ldrb r6, [r1, r6, asr #NBR-5] @ r6 = r = sat5[Y4>>scale + rv'] + + /* calculate pixel_4 and pack with pixel_3 before writing */ + orr r3, r3, r4, lsl #16 @ r3 = pixel_4 << 16 | pixel_3 + orr r3, r3, r5, lsl #21 + orr r3, r3, r6, lsl #27 + + /* write pixel_3 and pixel_4 */ + str r3, [r9], #4 @ *dst1_p++ = r3 + + /* check for loop end */ + subs r7, r7, #0x10000 @ loop_count-- + bge loop_start @ back to beginning + + /* bye */ + add sp, sp, #STACK_SZ @ deallocate stack + ldmpc regs=r4-r11 @ restore registers + + .ltorg + .size lcd_write_yuv420_lines, .-lcd_write_yuv420_lines + +/* data */ + .align 2 +const_data: + .word COEF_C0 + .word CONST_MLA_GUV + .word COEF_C3_C2 + .word CONST_MLA_BU + .word COEF_C4_C1 + .word CONST_MLA_RV + .word table_sat5 + + .size const_data, .-const_data + +/* saturation tables */ + /*.section .data*/ + /* aligned to cache line size to minimize cache usage */ + .align CACHEALIGN_BITS + +saturation_tables: + /* 5-bit saturation table [-36..0..+67], size=104 */ + /* table_sat5[-36..-1] */ + .byte 0, 0, 0, 0 + .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + table_sat5: + /* table_sat5[0..67] */ + .byte 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 + .byte 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 + .byte 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31 + .byte 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31 + .byte 31, 31, 31, 31 + + /* 6-bit saturation table [-44..0..+107], size=152 */ + /* table_sat6[-44..-1] */ + .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + table_sat6: + /* table_sat6[0..107] */ + .byte 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 + .byte 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 + .byte 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47 + .byte 48, 49, 50, 51, 52, 53 ,54, 55, 56, 57, 58, 59, 60, 61, 62, 63 + .byte 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 + .byte 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 + .byte 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 + + .size saturation_tables, .-saturation_tables +#endif /* YUV2RGB_VERSION */ diff --git a/firmware/target/arm/s5l8702/ipod6g/lcd-asm-ipod6g.S b/firmware/target/arm/s5l8702/ipod6g/lcd-asm-ipod6g.S deleted file mode 100644 index 6ee90098af..0000000000 --- a/firmware/target/arm/s5l8702/ipod6g/lcd-asm-ipod6g.S +++ /dev/null @@ -1,1013 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id: lcd-as-video.S 26756 2010-06-11 04:41:36Z funman $ - * - * Copyright (C) 2010 by Andree Buschmann - * - * 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. - * - ****************************************************************************/ - -/* Version history: - * - * SVN: - * - initial SVN version. - * - * ARMv4: - * - use all available registers to calculate four pixels within each - * loop iteration. - * - avoid LDR interlocks. - * - * ARMv5TE: - * - use ARMv5TE+ 1-cycle multiply-accumulate instructions. - * - * ARMv5TE_WST: - * - use data tables (256 bytes) for RBG565 saturation. - * - * All versions are based on current SVN algorithm (round->scale->add) - * using the same coefficients, so output results are identical. - * - * TODO?: SVN coefficients are a very nice approximation for operations - * with shift+add instructions. When 16x16+32 MLA instructions are used, - * NBR and COEF_N could probably be adjusted to slighly increase accuracy. - */ -#define VERSION_SVN 0 -#define VERSION_ARMV4 1 -#define VERSION_ARMV5TE 2 -#define VERSION_ARMV5TE_WST 3 - -#define YUV2RGB_VERSION VERSION_ARMV5TE_WST - - -#define ASM -#include "config.h" -#include "cpu.h" - -#if (YUV2RGB_VERSION == VERSION_SVN) - .section .icode, "ax", %progbits - - -/**************************************************************************** - * extern void lcd_write_yuv420_lines(unsigned char const * const src[3], - * uint16_t* out, - * int width, - * int stride); - * - * Conversion from Motion JPEG and MPEG Y'PbPr to RGB is: - * |R| |1.164 0.000 1.596| |Y' - 16| - * |G| = |1.164 -0.391 -0.813| |Pb - 128| - * |B| |1.164 2.018 0.000| |Pr - 128| - * - * Scaled, normalized, rounded and tweaked to yield RGB 565: - * |R| |74 0 101| |Y' - 16| >> 9 - * |G| = |74 -24 -51| |Cb - 128| >> 8 - * |B| |74 128 0| |Cr - 128| >> 9 - * - * Converts two lines from YUV to RGB565 and writes to LCD at once. First loop - * loads Cb/Cr, calculates the chroma offset and saves them to buffer. Within - * the second loop these chroma offset are reloaded from buffer. Within each - * loop two pixels are calculated and written to LCD. - */ - .align 2 - .global lcd_write_yuv420_lines - .type lcd_write_yuv420_lines, %function -lcd_write_yuv420_lines: - /* r0 = src = yuv_src */ - /* r1 = dst = out */ - /* r2 = width */ - /* r3 = stride */ - stmfd sp!, { r4-r10, lr } /* save non-scratch */ - ldmia r0, { r9, r10, r12 } /* r9 = yuv_src[0] = Y'_p */ - /* r10 = yuv_src[1] = Cb_p */ - /* r12 = yuv_src[2] = Cr_p */ - add r3, r9, r3 /* r3 = &ysrc[stride] */ - add r4, r2, r2, asr #1 /* chroma buffer lenght = width/2 *3 */ - mov r4, r4, asl #2 /* use words for str/ldm possibility */ - add r4, r4, #15 /* plus room for 3 additional words, */ - bic r4, r4, #3 /* rounded up to multiples of 4 byte */ - sub sp, sp, r4 /* and allocate on stack */ - stmia sp, {r2-r4} /* width, &ysrc[stride], stack_alloc */ - - mov r7, r2 /* r7 = loop count */ - add r8, sp, #12 /* chroma buffer */ - mov lr, r1 /* RGB565 data destination buffer */ - - /* 1st loop start */ -10: /* loop start */ - - ldrb r0, [r10], #1 /* r0 = *usrc++ = *Cb_p++ */ - ldrb r1, [r12], #1 /* r1 = *vsrc++ = *Cr_p++ */ - - sub r0, r0, #128 /* r0 = Cb-128 */ - sub r1, r1, #128 /* r1 = Cr-128 */ - - add r2, r1, r1, asl #1 /* r2 = Cr*51 + Cb*24 */ - add r2, r2, r2, asl #4 - add r2, r2, r0, asl #3 - add r2, r2, r0, asl #4 - - add r4, r1, r1, asl #2 /* r1 = Cr*101 */ - add r4, r4, r1, asl #5 - add r1, r4, r1, asl #6 - - add r1, r1, #256 /* r1 = rv = (r1 + 256) >> 9 */ - mov r1, r1, asr #9 - rsb r2, r2, #128 /* r2 = guv = (-r2 + 128) >> 8 */ - mov r2, r2, asr #8 - add r0, r0, #2 /* r0 = bu = (Cb*128 + 256) >> 9 */ - mov r0, r0, asr #2 - stmia r8!, {r0-r2} /* store r0, r1 and r2 to chroma buffer */ - - /* 1st loop, first pixel */ - ldrb r5, [r9], #1 /* r5 = *ysrc++ = *Y'_p++ */ - sub r5, r5, #16 /* r5 = (Y'-16) * 74 */ - add r3, r5, r5, asl #2 - add r5, r3, r5, asl #5 - - add r6, r1, r5, asr #8 /* r6 = r = (Y >> 9) + rv */ - add r3, r2, r5, asr #7 /* r3 = g = (Y >> 8) + guv */ - add r4, r0, r5, asr #8 /* r4 = b = (Y >> 9) + bu */ - - orr r5, r6, r4 /* check if clamping is needed... */ - orr r5, r5, r3, asr #1 /* ...at all */ - cmp r5, #31 - bls 15f /* -> no clamp */ - cmp r6, #31 /* clamp r */ - mvnhi r6, r6, asr #31 - andhi r6, r6, #31 - cmp r3, #63 /* clamp g */ - mvnhi r3, r3, asr #31 - andhi r3, r3, #63 - cmp r4, #31 /* clamp b */ - mvnhi r4, r4, asr #31 - andhi r4, r4, #31 -15: /* no clamp */ - - /* calculate pixel_1 and save to r4 for later pixel packing */ - orr r4, r4, r3, lsl #5 /* pixel_1 = r<<11 | g<<5 | b */ - orr r4, r4, r6, lsl #11 /* r4 = pixel_1 */ - - /* 1st loop, second pixel */ - ldrb r5, [r9], #1 /* r5 = *ysrc++ = *Y'_p++ */ - sub r5, r5, #16 /* r5 = (Y'-16) * 74 */ - add r3, r5, r5, asl #2 - add r5, r3, r5, asl #5 - - add r6, r1, r5, asr #8 /* r6 = r = (Y >> 9) + rv */ - add r3, r2, r5, asr #7 /* r3 = g = (Y >> 8) + guv */ - add r5, r0, r5, asr #8 /* r5 = b = (Y >> 9) + bu */ - - orr r0, r6, r5 /* check if clamping is needed... */ - orr r0, r0, r3, asr #1 /* ...at all */ - cmp r0, #31 - bls 15f /* -> no clamp */ - cmp r6, #31 /* clamp r */ - mvnhi r6, r6, asr #31 - andhi r6, r6, #31 - cmp r3, #63 /* clamp g */ - mvnhi r3, r3, asr #31 - andhi r3, r3, #63 - cmp r5, #31 /* clamp b */ - mvnhi r5, r5, asr #31 - andhi r5, r5, #31 -15: /* no clamp */ - - /* calculate pixel_2 and pack with pixel_1 before writing */ - orr r5, r5, r3, lsl #5 /* pixel_2 = r<<11 | g<<5 | b */ - orr r5, r5, r6, lsl #11 /* r5 = pixel_2 */ - orr r4, r4, r5, lsl #16 - str r4, [lr], #4 /* write pixel_1 and pixel_2 */ - - subs r7, r7, #2 /* check for loop end */ - bgt 10b /* back to beginning */ - /* 1st loop end */ - - /* Reload several registers for pointer rewinding for next loop */ - add r8, sp, #12 /* chroma buffer */ - ldmia sp, {r7, r9} /* r7 = loop count */ - /* r9 = &ysrc[stride] */ - - /* 2nd loop start */ -20: /* loop start */ - /* restore r0 (bu), r1 (rv) and r2 (guv) from chroma buffer */ - ldmia r8!, {r0-r2} - - /* 2nd loop, first pixel */ - ldrb r5, [r9], #1 /* r5 = *ysrc++ = *Y'_p++ */ - sub r5, r5, #16 /* r5 = (Y'-16) * 74 */ - add r3, r5, r5, asl #2 - add r5, r3, r5, asl #5 - - add r6, r1, r5, asr #8 /* r6 = r = (Y >> 9) + rv */ - add r3, r2, r5, asr #7 /* r3 = g = (Y >> 8) + guv */ - add r4, r0, r5, asr #8 /* r4 = b = (Y >> 9) + bu */ - - orr r5, r6, r4 /* check if clamping is needed... */ - orr r5, r5, r3, asr #1 /* ...at all */ - cmp r5, #31 - bls 15f /* -> no clamp */ - cmp r6, #31 /* clamp r */ - mvnhi r6, r6, asr #31 - andhi r6, r6, #31 - cmp r3, #63 /* clamp g */ - mvnhi r3, r3, asr #31 - andhi r3, r3, #63 - cmp r4, #31 /* clamp b */ - mvnhi r4, r4, asr #31 - andhi r4, r4, #31 -15: /* no clamp */ - /* calculate pixel_1 and save to r4 for later pixel packing */ - orr r4, r4, r3, lsl #5 /* pixel_1 = r<<11 | g<<5 | b */ - orr r4, r4, r6, lsl #11 /* r4 = pixel_1 */ - - /* 2nd loop, second pixel */ - ldrb r5, [r9], #1 /* r5 = *ysrc++ = *Y'_p++ */ - sub r5, r5, #16 /* r5 = (Y'-16) * 74 */ - add r3, r5, r5, asl #2 - add r5, r3, r5, asl #5 - - add r6, r1, r5, asr #8 /* r6 = r = (Y >> 9) + rv */ - add r3, r2, r5, asr #7 /* r3 = g = (Y >> 8) + guv */ - add r5, r0, r5, asr #8 /* r5 = b = (Y >> 9) + bu */ - - orr r0, r6, r5 /* check if clamping is needed... */ - orr r0, r0, r3, asr #1 /* ...at all */ - cmp r0, #31 - bls 15f /* -> no clamp */ - cmp r6, #31 /* clamp r */ - mvnhi r6, r6, asr #31 - andhi r6, r6, #31 - cmp r3, #63 /* clamp g */ - mvnhi r3, r3, asr #31 - andhi r3, r3, #63 - cmp r5, #31 /* clamp b */ - mvnhi r5, r5, asr #31 - andhi r5, r5, #31 -15: /* no clamp */ - - /* calculate pixel_2 and pack with pixel_1 before writing */ - orr r5, r5, r3, lsl #5 /* pixel_2 = r<<11 | g<<5 | b */ - orr r5, r5, r6, lsl #11 /* r5 = pixel_2 */ - orr r4, r4, r5, lsl #16 - str r4, [lr], #4 /* write pixel_1 and pixel_2 */ - - subs r7, r7, #2 /* check for loop end */ - bgt 20b /* back to beginning */ - /* 2nd loop end */ - - ldr r3, [sp, #8] - add sp, sp, r3 /* deallocate buffer */ - ldmpc regs=r4-r10 /* restore registers */ - - .ltorg - .size lcd_write_yuv420_lines, .-lcd_write_yuv420_lines - - -#elif (YUV2RGB_VERSION == VERSION_ARMV4) -/**************************************************************************** - * extern void lcd_write_yuv420_lines(unsigned char const * const src[3], - * uint16_t* out, - * int width, - * int stride); - * - * Conversion from Motion JPEG and MPEG Y'PbPr to RGB is: - * |R| |1.164 0.000 1.596| |Y' - 16| - * |G| = |1.164 -0.391 -0.813| |Pb - 128| - * |B| |1.164 2.018 0.000| |Pr - 128| - * - * Scaled, normalized, rounded and tweaked to yield RGB 565: - * |R| |74 0 101| |Y' - 16| >> 9 - * |G| = |74 -24 -51| |Cb - 128| >> 8 - * |B| |74 128 0| |Cr - 128| >> 9 - * - * Converts two lines from YUV420 to RGB565, within each iteration four - * pixels (2 per line) are calculated and written to destination buffer. - */ - .section .icode, "ax", %progbits - - .align 2 - .global lcd_write_yuv420_lines - .type lcd_write_yuv420_lines, %function - -lcd_write_yuv420_lines: - /* r0 = src = yuv_src */ - /* r1 = dst = out */ - /* r2 = width */ - /* r3 = stride */ - stmfd sp!, {r4-r11,lr} /* save non-scratch */ - ldmia r0, {r10-r12} /* r10 = yuv_src[0] = Y'_p */ - /* r11 = yuv_src[1] = Cb_p */ - /* r12 = yuv_src[2] = Cr_p */ - mov r9, r2, lsl #1 /* r9 = 2*width (loop count) */ - str r9, [sp, #-4]! /* [--sp] = 2*width (constant) */ - add r8, r10, r3 /* r8 = Y'_p + stride = Y'stride_p */ - mov lr, r1 /* RGB565 data destination buffer */ - -10: /* loop start */ - ldrb r0, [r11], #1 /* r0 = *Cb_p++ */ - ldrb r1, [r12], #1 /* r1 = *Cr_p++ */ - ldrb r3, [r8], #1 /* r3 = Y'3 */ - ldrb r4, [r8], #1 /* r4 = Y'4 */ - - sub r0, r0, #128 /* r0 = Cb-128 */ - sub r1, r1, #128 /* r1 = Cr-128 */ - - add r2, r1, r1, asl #1 /* r2 = Cr*51 + Cb*24 */ - add r2, r2, r2, asl #4 - add r2, r2, r0, asl #3 - add r2, r2, r0, asl #4 - - add r5, r1, r1, asl #2 /* r1 = Cr*101 */ - add r5, r5, r1, asl #5 - add r1, r5, r1, asl #6 - - add r1, r1, #256 /* r1 = rv = (r1 + 256) >> 9 */ - mov r1, r1, asr #9 - rsb r2, r2, #128 /* r2 = guv = (-r2 + 128) >> 8 */ - mov r2, r2, asr #8 - add r0, r0, #2 /* r0 = bu = (Cb*128 + 256) >> 9 */ - mov r0, r0, asr #2 - - /* pixel_3 */ - sub r3, r3, #16 /* r3 = (Y'-16) * (74/2) */ - add r7, r3, r3, asl #2 - add r3, r7, r3, asl #5 - - add r6, r1, r3, asr #8 /* r6 = r = (Y >> 9) + rv */ - add r7, r2, r3, asr #7 /* r7 = g = (Y >> 8) + guv */ - add r5, r0, r3, asr #8 /* r5 = b = (Y >> 9) + bu */ - - orr r3, r6, r5 /* check if clamping is needed... */ - orr r3, r3, r7, asr #1 /* ...at all */ - cmp r3, #31 - bls 15f /* no clamp */ - cmp r6, #31 /* clamp r */ - mvnhi r6, r6, asr #31 - andhi r6, r6, #31 - cmp r7, #63 /* clamp g */ - mvnhi r7, r7, asr #31 - andhi r7, r7, #63 - cmp r5, #31 /* clamp b */ - mvnhi r5, r5, asr #31 - andhi r5, r5, #31 -15: /* no clamp */ - - /* calculate pixel_3 and save to r5 for later pixel packing */ - orr r5, r5, r7, lsl #5 /* pixel_3 = r<<11 | g<<5 | b */ - orr r5, r5, r6, lsl #11 /* r5 = pixel_3 */ - - /* pixel_4 */ - sub r4, r4, #16 /* r4 = (Y'-16) * (74/2) */ - add r7, r4, r4, asl #2 - add r4, r7, r4, asl #5 - - add r6, r1, r4, asr #8 /* r6 = r = (Y >> 9) + rv */ - add r7, r2, r4, asr #7 /* r7 = g = (Y >> 8) + guv */ - add r4, r0, r4, asr #8 /* r4 = b = (Y >> 9) + bu */ - - orr r3, r6, r4 /* check if clamping is needed... */ - orr r3, r3, r7, asr #1 /* ...at all */ - cmp r3, #31 - bls 15f /* no clamp */ - cmp r6, #31 /* clamp r */ - mvnhi r6, r6, asr #31 - andhi r6, r6, #31 - cmp r7, #63 /* clamp g */ - mvnhi r7, r7, asr #31 - andhi r7, r7, #63 - cmp r4, #31 /* clamp b */ - mvnhi r4, r4, asr #31 - andhi r4, r4, #31 -15: /* no clamp */ - - /* calculate pixel_4 and pack with pixel_3 before writing */ - orr r4, r4, r7, lsl #5 /* pixel_4 = r<<11 | g<<5 | b */ - orr r4, r4, r6, lsl #11 /* r4 = pixel_4 */ - orr r5, r5, r4, lsl #16 /* r5 = pixel_4<<16 | pixel_3 */ - - ldr r7, [sp] /* r7 = 2*width */ - ldrb r3, [r10], #1 /* r3 = Y'1 */ - ldrb r4, [r10], #1 /* r4 = Y'2 */ - - str r5, [lr, r7] /* write pixel_3 and pixel_4 */ - - /* pixel_1 */ - sub r3, r3, #16 /* r3 = (Y'-16) * (74/2) */ - add r7, r3, r3, asl #2 - add r3, r7, r3, asl #5 - - add r6, r1, r3, asr #8 /* r6 = r = (Y >> 9) + rv */ - add r7, r2, r3, asr #7 /* r7 = g = (Y >> 8) + guv */ - add r5, r0, r3, asr #8 /* r5 = b = (Y >> 9) + bu */ - - orr r3, r6, r5 /* check if clamping is needed... */ - orr r3, r3, r7, asr #1 /* ...at all */ - cmp r3, #31 - bls 15f /* no clamp */ - cmp r6, #31 /* clamp r */ - mvnhi r6, r6, asr #31 - andhi r6, r6, #31 - cmp r7, #63 /* clamp g */ - mvnhi r7, r7, asr #31 - andhi r7, r7, #63 - cmp r5, #31 /* clamp b */ - mvnhi r5, r5, asr #31 - andhi r5, r5, #31 -15: /* no clamp */ - - /* calculate pixel_1 and save to r5 for later pixel packing */ - orr r5, r5, r7, lsl #5 /* pixel_1 = r<<11 | g<<5 | b */ - orr r5, r5, r6, lsl #11 /* r5 = pixel_1 */ - - /* pixel_2 */ - sub r4, r4, #16 /* r4 = (Y'-16) * (74/2) */ - add r7, r4, r4, asl #2 - add r4, r7, r4, asl #5 - - add r6, r1, r4, asr #8 /* r6 = r = (Y >> 9) + rv */ - add r7, r2, r4, asr #7 /* r7 = g = (Y >> 8) + guv */ - add r4, r0, r4, asr #8 /* r4 = b = (Y >> 9) + bu */ - - orr r3, r6, r4 /* check if clamping is needed... */ - orr r3, r3, r7, asr #1 /* ...at all */ - cmp r3, #31 - bls 15f /* no clamp */ - cmp r6, #31 /* clamp r */ - mvnhi r6, r6, asr #31 - andhi r6, r6, #31 - cmp r7, #63 /* clamp g */ - mvnhi r7, r7, asr #31 - andhi r7, r7, #63 - cmp r4, #31 /* clamp b */ - mvnhi r4, r4, asr #31 - andhi r4, r4, #31 -15: /* no clamp */ - - /* calculate pixel_2 and pack with pixel_1 before writing */ - orr r4, r4, r7, lsl #5 /* pixel_2 = r<<11 | g<<5 | b */ - orr r4, r4, r6, lsl #11 /* r4 = pixel_2 */ - orr r5, r5, r4, lsl #16 /* r5 = pixel_2<<16 | pixel_1 */ - - str r5, [lr], #4 /* write pixel_1 and pixel_2 */ - - subs r9, r9, #4 /* check for loop end */ - bgt 10b /* back to beginning */ - - /* loop end */ - add sp, sp, #4 /* deallocate stack */ - ldmpc regs=r4-r11 /* restore registers */ - - .ltorg - .size lcd_write_yuv420_lines, .-lcd_write_yuv420_lines - - -#elif (YUV2RGB_VERSION == VERSION_ARMV5TE) -/**************************************************************************** - * How do I encode Y'CBCR components from R'G'B' in [0, +1]? (see ColorFAQ) - * |R| |0.00456621 0 0.00625893| |Y' - 16| - * |G| = |0.00456621 -0.00153632 -0.00318811| |Pb - 128| - * |B| |0.00456621 0.00791071 0 | |Pr - 128| - * - * Scaled, normalized, rounded and tweaked to yield RGB 565: - * |R| |74 0 101| |Y' - 16| >> 9 - * |G| = |74 -24 -51| |Cb - 128| >> 8 - * |B| |74 128 0| |Cr - 128| >> 9 - */ -#define NBR 14 /* 14-bit resolution (SVN) */ -#define COEF_C0 74 -#define COEF_C1 101 -#define COEF_C2 -24 -#define COEF_C3 -51 -#define COEF_C4 128 -#define C4_IS_POW2 - -/* constant for rounding a NBR number before down-scaling it to RS bits */ -#define ROUND(RS) (1 << (NBR - RS - 1)) - -/* packed 16-bit coefficients */ -#define COEF_C4_C1 ((COEF_C4 << 16) | (COEF_C1 & 0xffff)) -#define COEF_2C3_2C2 ((COEF_C3 << 17) | ((COEF_C2 << 1) & 0xffff)) -/* 32-bit MLA constants */ -#define CONST_MLA_Y (-16 * COEF_C0) - -/**************************************************************************** - * extern void lcd_write_yuv420_lines(unsigned char const * const src[3], - * uint16_t* out, - * int width, - * int stride); - * - * Converts two lines from YUV420 to RGB565, within each iteration four - * pixels (2 per line) are calculated and written to destination buffer. - * - * - use ARMv5TE+ 1-cycle multiply+accumulator instructions. - */ - .section .icode, "ax", %progbits - - .align 2 - .global lcd_write_yuv420_lines - .type lcd_write_yuv420_lines, %function - -lcd_write_yuv420_lines: - @ r0 = src = yuv_src - @ r1 = out = dst_p - @ r2 = width - @ r3 = stride - stmfd sp!, {r4-r11,lr} @ save non-scratch - ldmia r0, {r10-r12} @ r10 = yuv_src[0] = Y'_p - @ r11 = yuv_src[1] = Cb_p - @ r12 = yuv_src[2] = Cr_p - adr r0, const_data @ load constants - ldmia r0, {r5-r8} @ r5 = COEF_C4_C1 - @ r6 = COEF_2C3_2C2 - @ r7 = COEF_C0 - @ r8 = CONST_MLA_Y - sub r4, r12, r11 @ r4 = Cr_p-Cb_p - mov r9, r2, asl #1 @ r9 = 2*width - stmfd sp!, {r4-r6,r9} @ SP -> Cr_p-Cb_p - @ COEF_C4_C1 - @ COEF_2C3_2C2 - @ 2*width - add r12, r10, r3 @ r12 = Y'_p + stride = Y'stride_p - mov lr, r1 @ RGB565 data destination buffer - orr r9, r7, r2, lsl #15 @ loop_count = width/2; - @ r9 = loop_count<<16 | COEF_C0 - sub r9, r9, #0x10000 @ loop_count-- - -10: @ loop_start - - @ register usage: - @ r8 = CONST_MLA_Y - @ r9 = loop count<<16 | COEF_C0 - @ r10 = Y'_p - @ r11 = Cb_p - @ r12 = Y'stride_p - @ lr = dst_p - @ free: r0-r7 - - ldmia sp, {r2-r4} @ r2 = Cr_p-Cb_p - @ r3 = COEF_C4_C1 - @ r4 = COEF_2C3_2C2 - mov r5, #ROUND(5) @ r5 = round constant - - ldrb r6, [r12], #1 @ r6 = Y'3 - ldrb r7, [r12], #1 @ r7 = Y'4 - - ldrb r1, [r11, r2] @ r1 = Cr = *Cr_p++ - ldrb r0, [r11], #1 @ r0 = Cb = *Cb_p++ - - /* calculate Y3 and Y4 */ - smlabb r6, r6, r9, r8 @ r6 = Y3 = C0*Y'3 - C0*16 - smlabb r7, r7, r9, r8 @ r7 = Y4 = C0*Y'4 - C0*16 - - /* calculate rv, guv, bu */ - sub r1, r1, #128 @ r1 = Cr" = Cr-128 - sub r0, r0, #128 @ r0 = Cb" = Cb-128 - - smlabt r2, r1, r4, r5 @ r2 = guv" = Cr"*(2*C2) + - smlabb r2, r0, r4, r2 @ Cb"*(2*C3) + round - smlabb r1, r1, r3, r5 @ r1 = rv" = Cr"*C1 + round - #ifdef C4_IS_POW2 - add r0, r5, r0, asl #NBR-7 @ r0 = bu" = Cb"*C4 + round - #else - smlabt r0, r0, r3, r5 @ r0 = bu" = Cb"*C4 + round - #endif - - /* scale rv",guv",bu" */ - mov r2, r2, asr #NBR-5 @ r2 = guv = guv" >> scale - mov r1, r1, asr #NBR-5 @ r1 = rv = rv" >> scale - mov r0, r0, asr #NBR-5 @ r0 = bu = bu" >> scale - - @ register usage: - @ r8-r12,lr: pointers, counters - @ r0,r1,r2 = bu,rv,guv (rounded and scaled to RGB565) - @ r6,r7 = Y'3,Y'4 - @ free: r3-r5 - - /* pixel_3 */ - add r5, r1, r6, asr #NBR-5 @ r5 = r = (Y3 >> scale) + rv - add r4, r2, r6, asr #NBR-6 @ r4 = g = (Y3 >> scale) + guv - add r3, r0, r6, asr #NBR-5 @ r3 = b = (Y3 >> scale) + bu - - orr r6, r5, r3 @ check if clamping is needed... - orr r6, r6, r4, asr #1 @ ...at all - cmp r6, #31 - bls 15f @ no clamp - cmp r5, #31 @ clamp r - mvnhi r5, r5, asr #31 - andhi r5, r5, #31 - cmp r4, #63 @ clamp g - mvnhi r4, r4, asr #31 - andhi r4, r4, #63 - cmp r3, #31 @ clamp b - mvnhi r3, r3, asr #31 - andhi r3, r3, #31 -15: @ no clamp - - /* calculate pixel_3 and save to r3 for later pixel packing */ - orr r3, r3, r4, lsl #5 @ r3 = pixel_3 = r<<11 | g<<5 | b - orr r3, r3, r5, lsl #11 - - /* pixel_4 */ - add r5, r1, r7, asr #NBR-5 @ r5 = r = (Y4 >> scale) + rv - add r4, r2, r7, asr #NBR-6 @ r4 = g = (Y4 >> scale) + guv - add r7, r0, r7, asr #NBR-5 @ r7 = b = (Y4 >> scale) + bu - - orr r6, r5, r7 @ check if clamping is needed... - orr r6, r6, r4, asr #1 @ ...at all - cmp r6, #31 - bls 15f @ no clamp - cmp r5, #31 @ clamp r - mvnhi r5, r5, asr #31 - andhi r5, r5, #31 - cmp r4, #63 @ clamp g - mvnhi r4, r4, asr #31 - andhi r4, r4, #63 - cmp r7, #31 @ clamp b - mvnhi r7, r7, asr #31 - andhi r7, r7, #31 -15: @ no clamp - - /* calculate pixel_4 and pack with pixel_3 before writing */ - orr r7, r7, r4, lsl #5 @ r7 = pixel_4 = r<<11 | g<<5 | b - orr r7, r7, r5, lsl #11 - orr r3, r3, r7, lsl #16 @ r3 = pixel_4<<16 | pixel_3 - - /* avoid interlocks when writing pixel_3 and pixel_4 */ - ldr r5, [sp, #12] @ r5 = 2*width - - ldrb r6, [r10], #1 @ r6 = Y'1 - ldrb r7, [r10], #1 @ r7 = Y'2 - - /* write pixel_3 and pixel_4 */ - str r3, [lr, r5] @ [dst_p + 2*width] = r3 - - @ register usage: - @ r8-r12,lr: pointers, counters - @ r0,r1,r2 = bu,rv,guv (rounded and scaled to RGB565) - @ r6,r7 = Y'1,Y'2 - @ free: r3-r5 - - /* calculate Y1 and Y2 */ - smlabb r6, r6, r9, r8 @ r6 = Y1 = C0*Y'1 - C0*16 - smlabb r7, r7, r9, r8 @ r7 = Y2 = C0*Y'2 - C0*16 - - /* pixel_1 */ - add r5, r1, r6, asr #NBR-5 @ r5 = r = (Y1 >> scale) + rv - add r4, r2, r6, asr #NBR-6 @ r4 = g = (Y1 >> scale) + guv - add r3, r0, r6, asr #NBR-5 @ r3 = b = (Y1 >> scale) + bu - - orr r6, r5, r3 @ check if clamping is needed... - orr r6, r6, r4, asr #1 @ ...at all - cmp r6, #31 - bls 15f @ no clamp - cmp r5, #31 @ clamp r - mvnhi r5, r5, asr #31 - andhi r5, r5, #31 - cmp r4, #63 @ clamp g - mvnhi r4, r4, asr #31 - andhi r4, r4, #63 - cmp r3, #31 @ clamp b - mvnhi r3, r3, asr #31 - andhi r3, r3, #31 -15: @ no clamp - - /* calculate pixel_1 and save to r3 for later pixel packing */ - orr r3, r3, r4, lsl #5 @ r3 = pixel_1 = r<<11 | g<<5 | b - orr r3, r3, r5, lsl #11 - - /* pixel_2 */ - add r5, r1, r7, asr #NBR-5 @ r5 = r = (Y2 >> scale) + rv - add r4, r2, r7, asr #NBR-6 @ r4 = g = (Y2 >> scale) + guv - add r7, r0, r7, asr #NBR-5 @ r7 = b = (Y2 >> scale) + bu - - orr r6, r5, r7 @ check if clamping is needed... - orr r6, r6, r4, asr #1 @ ...at all - cmp r6, #31 - bls 15f @ no clamp - cmp r5, #31 @ clamp r - mvnhi r5, r5, asr #31 - andhi r5, r5, #31 - cmp r4, #63 @ clamp g - mvnhi r4, r4, asr #31 - andhi r4, r4, #63 - cmp r7, #31 @ clamp b - mvnhi r7, r7, asr #31 - andhi r7, r7, #31 -15: @ no clamp - - /* calculate pixel_2 and pack with pixel_1 before writing */ - orr r7, r7, r4, lsl #5 @ r7 = pixel_2 = r<<11 | g<<5 | b - orr r7, r7, r5, lsl #11 - orr r3, r3, r7, lsl #16 @ r3 = pixel_2 << 16 | pixel_1 - - str r3, [lr], #4 @ write pixel_1 and pixel_2 - - /* check for loop end */ - subs r9, r9, #0x10000 @ loop_count-- - bge 10b @ back to beginning - - /* bye */ - add sp, sp, #16 - ldmpc regs=r4-r11 @ restore registers - - .ltorg - .size lcd_write_yuv420_lines, .-lcd_write_yuv420_lines - -/* data */ - .align 2 -const_data: - .word COEF_C4_C1 - .word COEF_2C3_2C2 - .word COEF_C0 - .word CONST_MLA_Y - - .size const_data, .-const_data - - -#else /* YUV2RGB_VERSION == VERSION_ARMV5TE_WST */ -/**************************************************************************** - * How do I encode Y'CBCR components from R'G'B' in [0, +1]? (see ColorFAQ) - * |R| |0.00456621 0 0.00625893| |Y' - 16| - * |G| = |0.00456621 -0.00153632 -0.00318811| |Pb - 128| - * |B| |0.00456621 0.00791071 0 | |Pr - 128| - * - * Scaled, normalized, rounded and tweaked to yield RGB 565: - * |R| |74 0 101| |Y' - 16| >> 9 - * |G| = |74 -24 -51| |Cb - 128| >> 8 - * |B| |74 128 0| |Cr - 128| >> 9 - */ -#define NBR 14 /* 14-bit resolution (SVN) */ -#define COEF_C0 74 -#define COEF_C1 101 -#define COEF_C2 -24 -#define COEF_C3 -51 -#define COEF_C4 128 -#define C4_IS_POW2 - -/* packed 16-bit coefficients */ -#define COEF_C4_C1 ((COEF_C4 << 16) | (COEF_C1 & 0xffff)) -#define COEF_C3_C2 ((COEF_C3 << 16) | (COEF_C2 & 0xffff)) - -/* constant for rounding an NBR number before down-scaling it to RS bits */ -#define ROUND(RS) (1 << (NBR - RS - 1)) - -/* 32-bit MLA constants */ -#define CONST_MLA_Y (-16 * COEF_C0) -#define CONST_MLA_RV ((-128 * COEF_C1) + ROUND(5)) -#define CONST_MLA_BU ((-128 * COEF_C4) + ROUND(5)) -/* trick to save the register needed for table_sat6 reference: - add table_sat6-table_sat5 offset (conveniently scaled) to guv MLA */ -#define CONST_MLA_GUV (-128 * (COEF_C2 + COEF_C3) + ROUND(6) + \ - ((table_sat6 - table_sat5) << (NBR - 6))) - -/**************************************************************************** - * extern void lcd_write_yuv420_lines(unsigned char const * const src[3], - * uint16_t* out, - * int width, - * int stride); - * - * Converts two lines from YUV420 to RGB565, within each iteration four - * pixels (2 per line) are calculated and written to destination buffer. - * - * - use ARMv5TE+ 1-cycle multiply+accumulator instructions. - * - use data tables (256 bytes) for RBG565 saturation. - */ - .section .icode, "ax", %progbits - - .align 2 - .global lcd_write_yuv420_lines - .type lcd_write_yuv420_lines, %function - -lcd_write_yuv420_lines: - @ r0 = src = yuv_src - @ r1 = out = dst1_p - @ r2 = width - @ r3 = stride - stmfd sp!, {r4-r11,lr} @ save non-scratch - ldmia r0, {r10-r12} @ r10 = yuv_src[0] = Y'_p - @ r11 = yuv_src[1] = Cb_p - @ r12 = yuv_src[2] = Cr_p - /* prepare data and fill stack */ - adr r0, const_data @ load constants - ldmia r0, {r4-r9,lr} @ r4 = COEF_C0 - @ r5 = CONST_MLA_GUV - @ r6 = COEF_C3_C2 - @ r7 = CONST_MLA_BU - @ r8 = COEF_C4_C1 - @ r9 = CONST_MLA_RV - @ lr = table_sat5 - sub r0, r12, r11 @ r0 = Cr_p-Cb_p - #define STACK_SZ 28 - stmfd sp!, {r0,r5-r9,lr} @ SP -> Cr_p-Cb_p - @ CONST_MLA_GUV - @ COEF_C3_C2 - @ CONST_MLA_BU - @ COEF_C4_C1 - @ CONST_MLA_RV - @ table_sat5 - mov r8, r4, lsl #4 @ - rsb r8, #0 @ r8 = -16*COEF_C0 = CONST_MLA_Y - mov lr, r1 @ RGB565 data destination buffer - add r9, lr, r2, asl #1 @ r9 = out + 2*width = dst2_p - add r12, r3, r10 @ r12 = Y'_p + stride - orr r7, r4, r2, lsl #15 @ loop_count = width/2; - @ r7 = loop_count<<16 | COEF_C0 - sub r7, r7, #0x10000 @ loop_count-- - - /* align loop code to minimize occupied lines, execution - time per loop is optimized ~10% on ARM926EJ-S */ - .align CACHEALIGN_BITS -loop_start: - - @ register usage: - @ r7 = loop count<<16 | COEF_C0 - @ r8 = CONST_MLA_Y - @ r9 = dst2_p - @ r10 = Y'_p - @ r11 = Cb_p - @ r12 = Y'stride_p - @ lr = dst1_p - @ free: r0-r6 - - /* load constants from stack */ - ldmia sp, {r1-r3,r6} @ r1 = Cr_p-Cb_p - @ r2 = CONST_MLA_GUV - @ r3 = COEF_C3_C2 - @ r6 = CONST_MLA_BU - - /* read Cr", Cb" */ - ldrb r1, [r11, r1] @ r1 = Cr = *Cr_p++ - ldrb r0, [r11], #1 @ r0 = Cb = *Cb_p++ - - /* load more constants (avoids r1 interlock) */ - ldrd r4, [sp, #16] @ r4 = COEF_C4_C1 - @ r5 = CONST_MLA_RV - - /* calculate rv", guv", bu" */ - smlabt r2, r1, r3, r2 @ r2 = guv" = Cr*C2 + Cb*C3 - smlabb r2, r0, r3, r2 @ + CONST_MLA_GUV - smlabb r1, r1, r4, r5 @ r1 = rv" = Cr*C1 + CONST_MLA_RV - #ifdef C4_IS_POW2 - add r0, r6, r0, asl #NBR-7 @ r0 = bu" = Cb*C4 + CONST_MLA_BU - #else - smlabt r0, r0, r4, r6 @ r0 = bu" = Cb*C4 + CONST_MLA_BU - #endif - - ldr r4, [sp, #STACK_SZ-4] @ r4 = table_sat5 - - /* read Y'1 and Y'2 */ - ldrb r5, [r10], #1 @ r5 = Y'1 = *Y'_p++ - ldrb r6, [r10], #1 @ r6 = Y'2 = *Y'_p++ - - /* scale rv",guv",bu", adding sat5_p here saves instructions later */ - add r1, r4, r1, asr #NBR-5 @ r1 = rv' = sat5_p + rv">>scale - add r2, r4, r2, asr #NBR-6 @ r2 = guv' = sat5_p + guv">>scale - add r0, r4, r0, asr #NBR-5 @ r0 = bu' = sat5_p + bu">>scale - - @ register usage: - @ r7-r12,lr: pointers, counters, tables - @ r0,r1,r2 = (bu,rv,guv) rounded and RGB565 scaled - @ r5,r6 = Y'1,Y'2 - @ free: r3,r4 - - /* calculate Y1 and Y2 */ - smlabb r5, r5, r7, r8 @ r5 = Y1 = C0*Y'1 - 16*C0 - smlabb r6, r6, r7, r8 @ r6 = Y2 = C0*Y'2 - 16*C0 - - /* pixel_1 */ - ldrb r3, [r0, r5, asr #NBR-5] @ r3 = b = sat5[Y1>>scale + bu'] - ldrb r4, [r2, r5, asr #NBR-6] @ r4 = g = sat6[Y1>>scale + guv'] - ldrb r5, [r1, r5, asr #NBR-5] @ r5 = r = sat5[Y1>>scale + rv'] - - /* calculate pixel_1 */ - orr r3, r3, r4, lsl #5 @ r3 = pixel_1 = g<<5 | b - - /* pixel_2 (avoid r5 interlock) */ - ldrb r4, [r0, r6, asr #NBR-5] @ r4 = b = sat5[Y2>>scale + bu'] - - /* calculate pixel_1 and save to r3 for later pixel packing */ - orr r3, r3, r5, lsl #11 @ r3 = pixel_1 = r<<11 | g<<5 | b - - /* pixel_2 */ - ldrb r5, [r2, r6, asr #NBR-6] @ r5 = g = sat6[Y2>>scale + guv'] - ldrb r6, [r1, r6, asr #NBR-5] @ r6 = r = sat5[Y2>>scale + rv'] - - /* calculate pixel_2 and pack with pixel_1 before writing */ - orr r3, r3, r4, lsl #16 @ r3 = pixel_2<<16 | pixel_1 - orr r3, r3, r5, lsl #21 - orr r3, r3, r6, lsl #27 - - /* read Y'3 and Y'4 */ - ldrb r5, [r12], #1 @ r5 = Y'3 = *Y'stride_p++ - ldrb r6, [r12], #1 @ r6 = Y'4 = *Y'stride_p++ - - /* write pixel_1 and pixel_2 */ - str r3, [lr], #4 @ *dst2_p++ = r3 - - @ register usage: - @ r7-r12,lr: pointers, counters, tables - @ r0,r1,r2 = (bu,rv,guv) rounded and RGB565 scaled - @ r5,r6 = Y'3,Y'4 - @ free: r3,r4 - - /* calculate Y3 and Y4 */ - smlabb r5, r5, r7, r8 @ r5 = Y3 = C0*Y'3 - 16*C0 - smlabb r6, r6, r7, r8 @ r6 = Y4 = C0*Y'4 - 16*C0 - - /* pixel_3 */ - ldrb r3, [r0, r5, asr #NBR-5] @ r3 = b = sat5[Y3>>scale + bu'] - ldrb r4, [r2, r5, asr #NBR-6] @ r4 = g = sat6[Y3>>scale + guv'] - ldrb r5, [r1, r5, asr #NBR-5] @ r5 = r = sat5[Y3>>scale + rv'] - - /* calculate pixel_3 */ - orr r3, r3, r4, lsl #5 @ r3 = pixel_3 = g<<5 | b - - /* pixel_4 (avoid r5 interlock) */ - ldrb r4, [r0, r6, asr #NBR-5] @ r4 = b = sat5[Y4>>scale + bu'] - - /* calculate pixel_3 and save to r3 for later pixel packing */ - orr r3, r3, r5, lsl #11 @ r3 = pixel_3 = r<<11 | g<<5 | b - - /* pixel_4 */ - ldrb r5, [r2, r6, asr #NBR-6] @ r5 = g = sat6[Y4>>scale + guv'] - ldrb r6, [r1, r6, asr #NBR-5] @ r6 = r = sat5[Y4>>scale + rv'] - - /* calculate pixel_4 and pack with pixel_3 before writing */ - orr r3, r3, r4, lsl #16 @ r3 = pixel_4 << 16 | pixel_3 - orr r3, r3, r5, lsl #21 - orr r3, r3, r6, lsl #27 - - /* write pixel_3 and pixel_4 */ - str r3, [r9], #4 @ *dst1_p++ = r3 - - /* check for loop end */ - subs r7, r7, #0x10000 @ loop_count-- - bge loop_start @ back to beginning - - /* bye */ - add sp, sp, #STACK_SZ @ deallocate stack - ldmpc regs=r4-r11 @ restore registers - - .ltorg - .size lcd_write_yuv420_lines, .-lcd_write_yuv420_lines - -/* data */ - .align 2 -const_data: - .word COEF_C0 - .word CONST_MLA_GUV - .word COEF_C3_C2 - .word CONST_MLA_BU - .word COEF_C4_C1 - .word CONST_MLA_RV - .word table_sat5 - - .size const_data, .-const_data - -/* saturation tables */ - /*.section .data*/ - /* aligned to cache line size to minimize cache usage */ - .align CACHEALIGN_BITS - -saturation_tables: - /* 5-bit saturation table [-36..0..+67], size=104 */ - /* table_sat5[-36..-1] */ - .byte 0, 0, 0, 0 - .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - table_sat5: - /* table_sat5[0..67] */ - .byte 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 - .byte 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 - .byte 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31 - .byte 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31 - .byte 31, 31, 31, 31 - - /* 6-bit saturation table [-44..0..+107], size=152 */ - /* table_sat6[-44..-1] */ - .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - table_sat6: - /* table_sat6[0..107] */ - .byte 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 - .byte 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 - .byte 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47 - .byte 48, 49, 50, 51, 52, 53 ,54, 55, 56, 57, 58, 59, 60, 61, 62, 63 - .byte 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 - .byte 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 - .byte 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 - - .size saturation_tables, .-saturation_tables -#endif /* YUV2RGB_VERSION */ diff --git a/firmware/target/arm/s5l8702/ipod6g/lcd-ipod6g.c b/firmware/target/arm/s5l8702/ipod6g/lcd-ipod6g.c deleted file mode 100644 index 14647a5697..0000000000 --- a/firmware/target/arm/s5l8702/ipod6g/lcd-ipod6g.c +++ /dev/null @@ -1,578 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id: lcd-nano2g.c 28868 2010-12-21 06:59:17Z Buschel $ - * - * Copyright (C) 2009 by Dave Chapman - * - * 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 "hwcompat.h" -#include "kernel.h" -#include "lcd.h" -#include "system.h" -#include "cpu.h" -#include "pmu-target.h" -#include "power.h" -#include "string.h" -#include "dma-s5l8702.h" - - -#define R_HORIZ_GRAM_ADDR_SET 0x200 -#define R_VERT_GRAM_ADDR_SET 0x201 -#define R_WRITE_DATA_TO_GRAM 0x202 -#define R_HORIZ_ADDR_START_POS 0x210 -#define R_HORIZ_ADDR_END_POS 0x211 -#define R_VERT_ADDR_START_POS 0x212 -#define R_VERT_ADDR_END_POS 0x213 - - -/* LCD type 1 register defines */ - -#define R_COLUMN_ADDR_SET 0x2a -#define R_ROW_ADDR_SET 0x2b -#define R_MEMORY_WRITE 0x2c - - -/** globals **/ - -int lcd_type; /* also needed in debug-s5l8702.c */ -static struct mutex lcd_mutex; -static uint16_t lcd_dblbuf[LCD_HEIGHT][LCD_WIDTH] CACHEALIGN_ATTR; -static bool lcd_ispowered; - -#define CMD 0 /* send command with N data */ -#define MREG 1 /* write multiple registers */ -#define SLEEP 2 -#define END 0xff - -#define CMD16(len) (CMD | ((len) << 8)) -#define MREG16(len) (MREG | ((len) << 8)) -#define SLEEP16(t) (SLEEP | ((t) << 8)) - -/* powersave sequences */ - -static const unsigned char lcd_sleep_seq_01[] = -{ - CMD, 0x28, 0, /* Display Off */ - SLEEP, 5, /* 50 ms */ - CMD, 0x10, 0, /* Sleep In Mode */ - SLEEP, 5, /* 50 ms */ - END -}; - -static const unsigned short lcd_enter_deepstby_seq_23[] = -{ - /* Display Off */ - MREG16(1), 0x007, 0x0172, - MREG16(1), 0x030, 0x03ff, - SLEEP16(9), - MREG16(1), 0x007, 0x0120, - MREG16(1), 0x030, 0x0000, - MREG16(1), 0x100, 0x0780, - MREG16(1), 0x007, 0x0000, - MREG16(1), 0x101, 0x0260, - MREG16(1), 0x102, 0x00a9, - SLEEP16(3), - MREG16(1), 0x100, 0x0700, - - /* Deep Standby Mode */ - MREG16(1), 0x100, 0x0704, - SLEEP16(5), - END -}; - -/* init sequences */ - -#ifdef HAVE_LCD_SLEEP -static const unsigned char lcd_awake_seq_01[] = -{ - CMD, 0x11, 0, /* Sleep Out Mode */ - SLEEP, 6, /* 60 ms */ - CMD, 0x29, 0, /* Display On */ - END -}; -#endif - -#if defined(HAVE_LCD_SLEEP) || defined(BOOTLOADER) -static const unsigned short lcd_init_seq_23[] = -{ - /* Display settings */ - MREG16(1), 0x008, 0x0808, - MREG16(7), 0x010, 0x0013, 0x0300, 0x0101, 0x0a03, 0x0a0e, 0x0a19, 0x2402, - MREG16(1), 0x018, 0x0001, - MREG16(1), 0x090, 0x0021, - - /* Gamma settings */ - MREG16(14), 0x300, 0x0307, 0x0003, 0x0402, 0x0303, 0x0300, 0x0407, 0x1c04, - 0x0307, 0x0003, 0x0402, 0x0303, 0x0300, 0x0407, 0x1c04, - MREG16(14), 0x310, 0x0707, 0x0407, 0x0306, 0x0303, 0x0300, 0x0407, 0x1c01, - 0x0707, 0x0407, 0x0306, 0x0303, 0x0300, 0x0407, 0x1c01, - MREG16(14), 0x320, 0x0206, 0x0102, 0x0404, 0x0303, 0x0300, 0x0407, 0x1c1f, - 0x0206, 0x0102, 0x0404, 0x0303, 0x0300, 0x0407, 0x1c1f, - - /* GRAM and Base Imagen settings (ili9326ds) */ - MREG16(2), 0x400, 0x001d, 0x0001, - MREG16(1), 0x205, 0x0060, - - /* Power settings */ - MREG16(1), 0x007, 0x0001, - MREG16(1), 0x031, 0x0071, - MREG16(1), 0x110, 0x0001, - MREG16(6), 0x100, 0x17b0, 0x0220, 0x00bd, 0x1500, 0x0103, 0x0105, - - /* Display On */ - MREG16(1), 0x007, 0x0021, - MREG16(1), 0x001, 0x0110, - MREG16(1), 0x003, 0x0230, - MREG16(1), 0x002, 0x0500, - MREG16(1), 0x007, 0x0031, - MREG16(1), 0x030, 0x0007, - SLEEP16(3), - MREG16(1), 0x030, 0x03ff, - SLEEP16(6), - MREG16(1), 0x007, 0x0072, - SLEEP16(15), - MREG16(1), 0x007, 0x0173, - END -}; -#endif /* HAVE_LCD_SLEEP || BOOTLOADER */ - -#ifdef BOOTLOADER -static const unsigned char lcd_init_seq_0[] = -{ - CMD, 0x11, 0, /* Sleep Out Mode */ - SLEEP, 0x03, /* 30 ms */ - CMD, 0x35, 1, 0x00, /* TEON (TBC) */ - CMD, 0x3a, 1, 0x06, /* COLMOD (TBC) */ - CMD, 0x36, 1, 0x00, /* MADCTR (TBC) */ - CMD, 0x13, 0, /* NORON: Normal Mode On (Partial - Mode Off, Scroll Mode Off) */ - CMD, 0x29, 0, /* Display On */ - END -}; - -static const unsigned char lcd_init_seq_1[] = -{ - CMD, 0xb0, 21, 0x3a, 0x3a, 0x80, 0x80, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x3c, 0x30, 0x0f, 0x00, - 0x01, 0x54, 0x06, 0x66, 0x66, - CMD, 0xb8, 1, 0xd8, - CMD, 0xb1, 30, 0x14, 0x59, 0x00, 0x15, 0x57, 0x27, 0x04, 0x85, - 0x14, 0x59, 0x00, 0x15, 0x57, 0x27, 0x04, 0x85, - 0x14, 0x09, 0x15, 0x57, 0x27, 0x04, 0x05, - 0x14, 0x09, 0x15, 0x57, 0x27, 0x04, 0x05, - CMD, 0xd2, 1, 0x01, - - /* Gamma settings (TBC) */ - CMD, 0xe0, 13, 0x00, 0x00, 0x00, 0x05, 0x0b, 0x12, 0x16, 0x1f, - 0x25, 0x22, 0x24, 0x29, 0x1c, - CMD, 0xe1, 13, 0x08, 0x01, 0x01, 0x06, 0x0b, 0x11, 0x15, 0x1f, - 0x27, 0x26, 0x29, 0x2f, 0x1e, - CMD, 0xe2, 13, 0x07, 0x01, 0x01, 0x05, 0x09, 0x0f, 0x13, 0x1e, - 0x26, 0x25, 0x28, 0x2e, 0x1e, - CMD, 0xe3, 13, 0x00, 0x00, 0x00, 0x05, 0x0b, 0x12, 0x16, 0x1f, - 0x25, 0x22, 0x24, 0x29, 0x1c, - CMD, 0xe4, 13, 0x08, 0x01, 0x01, 0x06, 0x0b, 0x11, 0x15, 0x1f, - 0x27, 0x26, 0x29, 0x2f, 0x1e, - CMD, 0xe5, 13, 0x07, 0x01, 0x01, 0x05, 0x09, 0x0f, 0x13, 0x1e, - 0x26, 0x25, 0x28, 0x2e, 0x1e, - - CMD, 0x3a, 1, 0x06, /* COLMOD (TBC) */ - CMD, 0xc2, 1, 0x00, /* Power Control 3 (TBC) */ - CMD, 0x35, 1, 0x00, /* TEON (TBC) */ - CMD, 0x11, 0, /* Sleep Out Mode */ - SLEEP, 0x06, /* 60 ms */ - CMD, 0x13, 0, /* NORON: Normal Mode On (Partial - Mode Off, Scroll Mode Off) */ - CMD, 0x29, 0, /* Display On */ - END -}; -#endif - -/* DMA configuration */ - -/* one single transfer at once, needed LLIs: - * screen_size / (DMAC_LLI_MAX_COUNT << swidth) = - * (320*240*2) / (4095*2) = 19 - */ -#define LCD_DMA_TSKBUF_SZ 1 /* N tasks, MUST be pow2 */ -#define LCD_DMA_LLIBUF_SZ 32 /* N LLIs, MUST be pow2 */ - -static struct dmac_tsk lcd_dma_tskbuf[LCD_DMA_TSKBUF_SZ]; -static struct dmac_lli volatile \ - lcd_dma_llibuf[LCD_DMA_LLIBUF_SZ] CACHEALIGN_ATTR; - -static struct dmac_ch lcd_dma_ch = { - .dmac = &s5l8702_dmac0, - .prio = DMAC_CH_PRIO(4), - .cb_fn = NULL, - - .tskbuf = lcd_dma_tskbuf, - .tskbuf_mask = LCD_DMA_TSKBUF_SZ - 1, - .queue_mode = QUEUE_NORMAL, - - .llibuf = lcd_dma_llibuf, - .llibuf_mask = LCD_DMA_LLIBUF_SZ - 1, - .llibuf_bus = DMAC_MASTER_AHB1, -}; - -static struct dmac_ch_cfg lcd_dma_ch_cfg = { - .srcperi = S5L8702_DMAC0_PERI_MEM, - .dstperi = S5L8702_DMAC0_PERI_LCD_WR, - .sbsize = DMACCxCONTROL_BSIZE_1, - .dbsize = DMACCxCONTROL_BSIZE_1, - .swidth = DMACCxCONTROL_WIDTH_16, - .dwidth = DMACCxCONTROL_WIDTH_16, - .sbus = DMAC_MASTER_AHB1, - .dbus = DMAC_MASTER_AHB1, - .sinc = DMACCxCONTROL_INC_ENABLE, - .dinc = DMACCxCONTROL_INC_DISABLE, - .prot = DMAC_PROT_CACH | DMAC_PROT_BUFF | DMAC_PROT_PRIV, - .lli_xfer_max_count = DMAC_LLI_MAX_COUNT, -}; - -static inline void s5l_lcd_write_reg(int cmd, unsigned int data) -{ - while (LCD_STATUS & 0x10); - LCD_WCMD = cmd; - - while (LCD_STATUS & 0x10); - /* 16-bit/1-transfer data format (ili9320ds s7.2.2) */ - LCD_WDATA = (data & 0x78ff) | - ((data & 0x0300) << 1) | ((data & 0x0400) << 5); -} - -static inline void s5l_lcd_write_cmd(unsigned short cmd) -{ - while (LCD_STATUS & 0x10); - LCD_WCMD = cmd; -} - -static inline void s5l_lcd_write_data(unsigned short data) -{ - while (LCD_STATUS & 0x10); - LCD_WDATA = data; -} - -static void lcd_run_seq8(const unsigned char *seq) -{ - int n; - - while (1) switch (*seq++) - { - case CMD: - s5l_lcd_write_cmd(*seq++); - n = *seq++; - while (n--) - s5l_lcd_write_data(*seq++); - break; - case SLEEP: - sleep(*seq++); - break; - case END: - default: - /* bye */ - return; - } -} - -static void lcd_run_seq16(const unsigned short *seq) -{ - int action, param; - unsigned short reg; - - while (1) - { - action = *seq & 0xff; - param = *seq++ >> 8; - - switch (action) - { - case MREG: - reg = *seq++; - while (param--) - s5l_lcd_write_reg(reg++, *seq++); - break; - case SLEEP: - sleep(param); - break; - case END: - default: - /* bye */ - return; - } - } -} - -/*** hardware configuration ***/ - -int lcd_default_contrast(void) -{ - return 0x1f; -} - -void lcd_set_contrast(int val) -{ - (void)val; -} - -void lcd_set_invert_display(bool yesno) -{ - (void)yesno; -} - -void lcd_set_flip(bool yesno) -{ - (void)yesno; -} - -bool lcd_active(void) -{ - return lcd_ispowered; -} - -void lcd_powersave(void) -{ - mutex_lock(&lcd_mutex); - - if (lcd_type & 2) - lcd_run_seq16(lcd_enter_deepstby_seq_23); - else - lcd_run_seq8(lcd_sleep_seq_01); - - /* mask lcd controller clock gate */ - PWRCON(0) |= (1 << CLOCKGATE_LCD); - - lcd_ispowered = false; - - mutex_unlock(&lcd_mutex); -} - -void lcd_shutdown(void) -{ - pmu_write(0x2b, 0); /* Kill the backlight, instantly. */ - pmu_write(0x29, 0); - - lcd_powersave(); -} - -#ifdef HAVE_LCD_SLEEP -void lcd_sleep(void) -{ - lcd_powersave(); -} - -void lcd_awake(void) -{ - mutex_lock(&lcd_mutex); - - /* unmask lcd controller clock gate */ - PWRCON(0) &= ~(1 << CLOCKGATE_LCD); - - if (lcd_type & 2) { - /* release from deep standby mode (ili9320ds s12.3) */ - for (int i = 0; i < 6; i++) { - s5l_lcd_write_cmd(0x000); - udelay(1000); - } - - lcd_run_seq16(lcd_init_seq_23); - } - else - lcd_run_seq8(lcd_awake_seq_01); - - lcd_ispowered = true; - - mutex_unlock(&lcd_mutex); - - send_event(LCD_EVENT_ACTIVATION, NULL); -} -#endif - -/* LCD init */ -void lcd_init_device(void) -{ - mutex_init(&lcd_mutex); - - /* unmask lcd controller clock gate */ - PWRCON(0) &= ~(1 << CLOCKGATE_LCD); - - /* Detect lcd type */ - lcd_type = (PDAT6 & 0x30) >> 4; - - while (!(LCD_STATUS & 0x2)); - LCD_CONFIG = 0x80100db0; - LCD_PHTIME = 0x33; - - /* Configure DMA channel */ - dmac_ch_init(&lcd_dma_ch, &lcd_dma_ch_cfg); - -#ifdef BOOTLOADER - switch (lcd_type) { - case 0: lcd_run_seq8(lcd_init_seq_0); break; - case 1: lcd_run_seq8(lcd_init_seq_1); break; - default: lcd_run_seq16(lcd_init_seq_23); break; - } -#endif - - lcd_ispowered = true; -} - -/*** Update functions ***/ - -static inline void lcd_write_pixel(fb_data pixel) -{ - mutex_lock(&lcd_mutex); - LCD_WDATA = pixel; - mutex_unlock(&lcd_mutex); -} - -/* Update the display. - This must be called after all other LCD functions that change the display. */ -void lcd_update(void) ICODE_ATTR; -void lcd_update(void) -{ - lcd_update_rect(0, 0, LCD_WIDTH, LCD_HEIGHT); -} - -/* Line write helper function. */ -extern void lcd_write_line(const fb_data *addr, - int pixelcount, - const unsigned int lcd_base_addr); - -static void displaylcd_setup(int x, int y, int width, int height) ICODE_ATTR; -static void displaylcd_setup(int x, int y, int width, int height) -{ - /* TODO: ISR()->panicf()->lcd_update() blocks forever */ - mutex_lock(&lcd_mutex); - while (dmac_ch_running(&lcd_dma_ch)) - yield(); - - int xe = (x + width) - 1; /* max horiz */ - int ye = (y + height) - 1; /* max vert */ - - if (lcd_type & 2) { - s5l_lcd_write_reg(R_HORIZ_ADDR_START_POS, x); - s5l_lcd_write_reg(R_HORIZ_ADDR_END_POS, xe); - s5l_lcd_write_reg(R_VERT_ADDR_START_POS, y); - s5l_lcd_write_reg(R_VERT_ADDR_END_POS, ye); - - s5l_lcd_write_reg(R_HORIZ_GRAM_ADDR_SET, x); - s5l_lcd_write_reg(R_VERT_GRAM_ADDR_SET, y); - - s5l_lcd_write_cmd(R_WRITE_DATA_TO_GRAM); - } else { - s5l_lcd_write_cmd(R_COLUMN_ADDR_SET); - s5l_lcd_write_data(x >> 8); - s5l_lcd_write_data(x & 0xff); - s5l_lcd_write_data(xe >> 8); - s5l_lcd_write_data(xe & 0xff); - - s5l_lcd_write_cmd(R_ROW_ADDR_SET); - s5l_lcd_write_data(y >> 8); - s5l_lcd_write_data(y & 0xff); - s5l_lcd_write_data(ye >> 8); - s5l_lcd_write_data(ye & 0xff); - - s5l_lcd_write_cmd(R_MEMORY_WRITE); - } -} - -static void displaylcd_dma(int pixels) ICODE_ATTR; -static void displaylcd_dma(int pixels) -{ - commit_dcache(); - dmac_ch_queue(&lcd_dma_ch, lcd_dblbuf, - (void*)S5L8702_DADDR_PERI_LCD_WR, pixels*2, NULL); - mutex_unlock(&lcd_mutex); -} - -/* Update a fraction of the display. */ -void lcd_update_rect(int, int, int, int) ICODE_ATTR; -void lcd_update_rect(int x, int y, int width, int height) -{ - int pixels = width * height; - fb_data* p = FBADDR(x,y); - uint16_t* out = lcd_dblbuf[0]; - -#ifdef HAVE_LCD_SLEEP - if (!lcd_active()) return; -#endif - - displaylcd_setup(x, y, width, height); - - /* Copy display bitmap to hardware */ - if (LCD_WIDTH == width) { - /* Write all lines at once */ - memcpy(out, p, pixels * 2); - } else { - do { - /* Write a single line */ - memcpy(out, p, width * 2); - p += LCD_WIDTH; - out += width; - } while (--height); - } - - displaylcd_dma(pixels); -} - -/* Line write helper function for lcd_yuv_blit. Writes two lines of yuv420. */ -extern void lcd_write_yuv420_lines(unsigned char const * const src[3], - uint16_t* outbuf, - int width, - int stride); - -/* Blit a YUV bitmap directly to the LCD */ -void lcd_blit_yuv(unsigned char * const src[3], - int src_x, int src_y, int stride, - int x, int y, int width, int height) ICODE_ATTR; -void lcd_blit_yuv(unsigned char * const src[3], - int src_x, int src_y, int stride, - int x, int y, int width, int height) -{ - unsigned int z; - unsigned char const * yuv_src[3]; - -#ifdef HAVE_LCD_SLEEP - if (!lcd_active()) return; -#endif - - width = (width + 1) & ~1; /* ensure width is even */ - - int pixels = width * height; - uint16_t* out = lcd_dblbuf[0]; - - z = stride * src_y; - yuv_src[0] = src[0] + z + src_x; - yuv_src[1] = src[1] + (z >> 2) + (src_x >> 1); - yuv_src[2] = src[2] + (yuv_src[1] - src[1]); - - displaylcd_setup(x, y, width, height); - - height >>= 1; - - do { - lcd_write_yuv420_lines(yuv_src, out, width, stride); - yuv_src[0] += stride << 1; - yuv_src[1] += stride >> 1; /* Skip down one chroma line */ - yuv_src[2] += stride >> 1; - out += width << 1; - } while (--height); - - displaylcd_dma(pixels); -} diff --git a/firmware/target/arm/s5l8702/ipod6g/piezo-6g.c b/firmware/target/arm/s5l8702/ipod6g/piezo-6g.c new file mode 100644 index 0000000000..06735673be --- /dev/null +++ b/firmware/target/arm/s5l8702/ipod6g/piezo-6g.c @@ -0,0 +1,125 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2006-2007 Robert Keevil + * + * 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 "system.h" +#include "kernel.h" +#include "piezo.h" + +static unsigned int duration; +static bool beeping; + +void INT_TIMERA(void) +{ + /* clear interrupt */ + TACON = TACON; + if (!(--duration)) + piezo_stop(); +} + +static void piezo_start(unsigned short cycles, unsigned short periods) +{ +#ifndef SIMULATOR + duration = periods; + beeping = 1; + /* select TA_OUT function on GPIO ports */ + PCON0 = (PCON0 & 0x00ffffff) | 0x53000000; + /* configure timer for 100 kHz (12 MHz / 4 / 30) */ + TACMD = (1 << 1); /* TA_CLR */ + TAPRE = 30 - 1; /* prescaler */ + TACON = (1 << 13) | /* TA_INT1_EN */ + (0 << 12) | /* TA_INT0_EN */ + (0 << 11) | /* TA_START */ + (1 << 8) | /* TA_CS = ECLK / 4 */ + (1 << 6) | /* select ECLK (12 MHz) */ + (1 << 4); /* TA_MODE_SEL = PWM mode */ + TADATA0 = cycles; /* set interval period */ + TADATA1 = cycles << 1; /* set interval period */ + TACMD = (1 << 0); /* TA_EN */ +#endif +} + +void piezo_stop(void) +{ +#ifndef SIMULATOR + beeping = 0; + TACMD = (1 << 1); /* TA_CLR */ + /* configure GPIO for the lowest power consumption */ + PCON0 = (PCON0 & 0x00ffffff) | 0xee000000; +#endif +} + +void piezo_clear(void) +{ + piezo_stop(); +} + +bool piezo_busy(void) +{ + return beeping; +} + +void piezo_init(void) +{ + piezo_stop(); +} + +void piezo_button_beep(bool beep, bool force) +{ + if (force) + while (beeping) + yield(); + + if (!beeping) + { + if (beep) + piezo_start(22, 457); + else + piezo_start(40, 4); + } +} + +#ifdef BOOTLOADER +void piezo_tone(uint32_t period /*uS*/, int32_t duration /*ms*/) +{ + int32_t stop = USEC_TIMER + duration*1000; + uint32_t level = 0; + + while ((int32_t)USEC_TIMER - stop < 0) + { + level ^= 1; + GPIOCMD = 0x0060e | level; + udelay(period >> 1); + } + + GPIOCMD = 0x0060e; +} + +void piezo_seq(uint16_t *seq) +{ + uint16_t period; + + while ((period = *seq++) != 0) + { + piezo_tone(period, *seq++); + udelay(*seq++ * 1000); + } +} +#endif diff --git a/firmware/target/arm/s5l8702/ipod6g/piezo-ipod6g.c b/firmware/target/arm/s5l8702/ipod6g/piezo-ipod6g.c deleted file mode 100644 index 06735673be..0000000000 --- a/firmware/target/arm/s5l8702/ipod6g/piezo-ipod6g.c +++ /dev/null @@ -1,125 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2006-2007 Robert Keevil - * - * 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 "system.h" -#include "kernel.h" -#include "piezo.h" - -static unsigned int duration; -static bool beeping; - -void INT_TIMERA(void) -{ - /* clear interrupt */ - TACON = TACON; - if (!(--duration)) - piezo_stop(); -} - -static void piezo_start(unsigned short cycles, unsigned short periods) -{ -#ifndef SIMULATOR - duration = periods; - beeping = 1; - /* select TA_OUT function on GPIO ports */ - PCON0 = (PCON0 & 0x00ffffff) | 0x53000000; - /* configure timer for 100 kHz (12 MHz / 4 / 30) */ - TACMD = (1 << 1); /* TA_CLR */ - TAPRE = 30 - 1; /* prescaler */ - TACON = (1 << 13) | /* TA_INT1_EN */ - (0 << 12) | /* TA_INT0_EN */ - (0 << 11) | /* TA_START */ - (1 << 8) | /* TA_CS = ECLK / 4 */ - (1 << 6) | /* select ECLK (12 MHz) */ - (1 << 4); /* TA_MODE_SEL = PWM mode */ - TADATA0 = cycles; /* set interval period */ - TADATA1 = cycles << 1; /* set interval period */ - TACMD = (1 << 0); /* TA_EN */ -#endif -} - -void piezo_stop(void) -{ -#ifndef SIMULATOR - beeping = 0; - TACMD = (1 << 1); /* TA_CLR */ - /* configure GPIO for the lowest power consumption */ - PCON0 = (PCON0 & 0x00ffffff) | 0xee000000; -#endif -} - -void piezo_clear(void) -{ - piezo_stop(); -} - -bool piezo_busy(void) -{ - return beeping; -} - -void piezo_init(void) -{ - piezo_stop(); -} - -void piezo_button_beep(bool beep, bool force) -{ - if (force) - while (beeping) - yield(); - - if (!beeping) - { - if (beep) - piezo_start(22, 457); - else - piezo_start(40, 4); - } -} - -#ifdef BOOTLOADER -void piezo_tone(uint32_t period /*uS*/, int32_t duration /*ms*/) -{ - int32_t stop = USEC_TIMER + duration*1000; - uint32_t level = 0; - - while ((int32_t)USEC_TIMER - stop < 0) - { - level ^= 1; - GPIOCMD = 0x0060e | level; - udelay(period >> 1); - } - - GPIOCMD = 0x0060e; -} - -void piezo_seq(uint16_t *seq) -{ - uint16_t period; - - while ((period = *seq++) != 0) - { - piezo_tone(period, *seq++); - udelay(*seq++ * 1000); - } -} -#endif diff --git a/firmware/target/arm/s5l8702/ipod6g/pmu-6g.c b/firmware/target/arm/s5l8702/ipod6g/pmu-6g.c new file mode 100644 index 0000000000..d282a48d5b --- /dev/null +++ b/firmware/target/arm/s5l8702/ipod6g/pmu-6g.c @@ -0,0 +1,469 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: pmu-nano2g.c 27752 2010-08-08 10:49:32Z bertrik $ + * + * Copyright © 2008 Rafaël Carré + * + * 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 "kernel.h" +#include "thread.h" + +#include "pmu-target.h" +#include "adc-target.h" +#include "i2c-s5l8702.h" +#include "gpio-s5l8702.h" + + +int pmu_read_multiple(int address, int count, unsigned char* buffer) +{ + return i2c_read(0, 0xe6, address, count, buffer); +} + +int pmu_write_multiple(int address, int count, unsigned char* buffer) +{ + return i2c_write(0, 0xe6, address, count, buffer); +} + +unsigned char pmu_read(int address) +{ + unsigned char tmp; + + pmu_read_multiple(address, 1, &tmp); + + return tmp; +} + +int pmu_write(int address, unsigned char val) +{ + return pmu_write_multiple(address, 1, &val); +} + +void pmu_ldo_on_in_standby(unsigned int ldo, int onoff) +{ + if (ldo < 4) + { + unsigned char newval = pmu_read(0x3B) & ~(1 << (2 * ldo)); + if (onoff) newval |= 1 << (2 * ldo); + pmu_write(0x3B, newval); + } + else if (ldo < 8) + { + unsigned char newval = pmu_read(0x3C) & ~(1 << (2 * (ldo - 4))); + if (onoff) newval |= 1 << (2 * (ldo - 4)); + pmu_write(0x3C, newval); + } +} + +void pmu_ldo_set_voltage(unsigned int ldo, unsigned char voltage) +{ + if (ldo > 6) return; + pmu_write(0x2d + (ldo << 1), voltage); +} + +void pmu_hdd_power(bool on) +{ + pmu_write(0x1b, on ? 1 : 0); +} + +void pmu_ldo_power_on(unsigned int ldo) +{ + if (ldo > 6) return; + pmu_write(0x2e + (ldo << 1), 1); +} + +void pmu_ldo_power_off(unsigned int ldo) +{ + if (ldo > 6) return; + pmu_write(0x2e + (ldo << 1), 0); +} + +void pmu_set_wake_condition(unsigned char condition) +{ + pmu_write(0xd, condition); +} + +void pmu_enter_standby(void) +{ + pmu_write(0xc, 1); +} + +void pmu_read_rtc(unsigned char* buffer) +{ + pmu_read_multiple(0x59, 7, buffer); +} + +void pmu_write_rtc(unsigned char* buffer) +{ + pmu_write_multiple(0x59, 7, buffer); +} + +/* + * ADC + */ +#define ADC_FULL_SCALE 2000 +#define ADC_FULL_SCALE_VISA 2400 +#define ADC_SUBTR_OFFSET 2250 + +static struct mutex pmu_adc_mutex; + +/* converts raw 8/10-bit value to millivolts */ +unsigned short pmu_adc_raw2mv( + const struct pmu_adc_channel *ch, unsigned short raw) +{ + int full_scale = ADC_FULL_SCALE; + int offset = 0; + + switch (ch->adcc1 & PCF5063X_ADCC1_ADCMUX_MASK) + { + case PCF5063X_ADCC1_MUX_BATSNS_RES: + case PCF5063X_ADCC1_MUX_ADCIN2_RES: + full_scale *= ((ch->adcc1 & PCF5063X_ADCC3_RES_DIV_MASK) == + PCF5063X_ADCC3_RES_DIV_TWO) ? 2 : 3; + break; + case PCF5063X_ADCC1_MUX_BATSNS_SUBTR: + case PCF5063X_ADCC1_MUX_ADCIN2_SUBTR: + offset = ADC_SUBTR_OFFSET; + break; + case PCF5063X_ADCC1_MUX_BATTEMP: + if (ch->adcc2 & PCF5063X_ADCC2_RATIO_BATTEMP) + full_scale = ADC_FULL_SCALE_VISA; + break; + case PCF5063X_ADCC1_MUX_ADCIN1: + if (ch->adcc2 & PCF5063X_ADCC2_RATIO_ADCIN1) + full_scale = ADC_FULL_SCALE_VISA; + break; + } + + int nrb = ((ch->adcc1 & PCF5063X_ADCC1_RES_MASK) == + PCF5063X_ADCC1_RES_8BIT) ? 8 : 10; + return (raw * full_scale / ((1<adcc3); + if (ch->bias_dly) + sleep(ch->bias_dly); + uint8_t buf[2] = { ch->adcc2, ch->adcc1 | PCF5063X_ADCC1_ADCSTART }; + pmu_write_multiple(PCF5063X_REG_ADCC2, 2, buf); + + int adcs3 = 0; + while (!(adcs3 & PCF5063X_ADCS3_ADCRDY)) + { + yield(); + adcs3 = pmu_read(PCF5063X_REG_ADCS3); + } + + int raw = pmu_read(PCF5063X_REG_ADCS1); + if ((ch->adcc1 & PCF5063X_ADCC1_RES_MASK) == PCF5063X_ADCC1_RES_10BIT) + raw = (raw << 2) | (adcs3 & PCF5063X_ADCS3_ADCDAT1L_MASK); + + mutex_unlock(&pmu_adc_mutex); + return raw; +} + +/* + * eINT + */ +#define Q_EINT 0 + +static char pmu_thread_stack[DEFAULT_STACK_SIZE/2]; +static struct event_queue pmu_queue; +static unsigned char ints_msk[6]; + +static void pmu_eint_isr(struct eint_handler*); + +static struct eint_handler pmu_eint = +{ + .gpio_n = GPIO_EINT_PMU, + .type = EIC_INTTYPE_LEVEL, + .level = EIC_INTLEVEL_LOW, + .isr = pmu_eint_isr, +}; + +static int pmu_input_holdswitch; + +int pmu_holdswitch_locked(void) +{ + return pmu_input_holdswitch; +} + +#ifdef IPOD_ACCESSORY_PROTOCOL +static int pmu_input_accessory; + +int pmu_accessory_present(void) +{ + return pmu_input_accessory; +} +#endif + +#if CONFIG_CHARGING +static int pmu_input_firewire; + +int pmu_firewire_present(void) +{ + return pmu_input_firewire; +} + +static void pmu_read_inputs_mbcs(void) +{ + pmu_input_firewire = !!(pmu_read(PCF5063X_REG_MBCS1) + & PCF5063X_MBCS1_ADAPTPRES); +} +#endif + +static void pmu_read_inputs_gpio(void) +{ + pmu_input_holdswitch = !(pmu_read(PCF50635_REG_GPIOSTAT) + & PCF50635_GPIOSTAT_GPIO2); +} + +static void pmu_read_inputs_ooc(void) +{ + unsigned char oocstat = pmu_read(PCF5063X_REG_OOCSTAT); + if (oocstat & PCF5063X_OOCSTAT_EXTON2) + usb_insert_int(); + else + usb_remove_int(); +#ifdef IPOD_ACCESSORY_PROTOCOL + pmu_input_accessory = !(oocstat & PCF5063X_OOCSTAT_EXTON3); +#endif +} + +static void pmu_eint_isr(struct eint_handler *h) +{ + eint_unregister(h); + queue_post(&pmu_queue, Q_EINT, 0); +} + +static void NORETURN_ATTR pmu_thread(void) +{ + struct queue_event ev; + unsigned char ints[6]; + + while (true) + { + queue_wait_w_tmo(&pmu_queue, &ev, TIMEOUT_BLOCK); + switch (ev.id) + { + case Q_EINT: + /* read (clear) PMU interrupts, this will also + raise the PMU IRQ pin */ + pmu_read_multiple(PCF5063X_REG_INT1, 2, ints); + ints[5] = pmu_read(PCF50635_REG_INT6); + +#if CONFIG_CHARGING + if (ints[0] & ~ints_msk[0]) pmu_read_inputs_mbcs(); +#endif + if (ints[1] & ~ints_msk[1]) pmu_read_inputs_ooc(); + if (ints[5] & ~ints_msk[5]) pmu_read_inputs_gpio(); + + eint_register(&pmu_eint); + break; + + case SYS_TIMEOUT: + break; + } + } +} + +/* main init */ +void pmu_init(void) +{ + mutex_init(&pmu_adc_mutex); + queue_init(&pmu_queue, false); + + create_thread(pmu_thread, + pmu_thread_stack, sizeof(pmu_thread_stack), 0, + "PMU" IF_PRIO(, PRIORITY_SYSTEM) IF_COP(, CPU)); + + /* configure PMU interrutps */ + for (int i = 0; i < 6; i++) + ints_msk[i] = 0xff; + +#if CONFIG_CHARGING + ints_msk[0] &= ~PCF5063X_INT1_ADPINS & /* FireWire */ + ~PCF5063X_INT1_ADPREM; +#endif + ints_msk[1] &= ~PCF5063X_INT2_EXTON2R & /* USB */ + ~PCF5063X_INT2_EXTON2F; +#ifdef IPOD_ACCESSORY_PROTOCOL + ints_msk[1] &= ~PCF5063X_INT2_EXTON3R & /* Accessory */ + ~PCF5063X_INT2_EXTON3F; +#endif + ints_msk[5] &= ~PCF50635_INT6_GPIO2; /* Holdswitch */ + + pmu_write_multiple(PCF5063X_REG_INT1M, 5, ints_msk); + pmu_write(PCF50635_REG_INT6M, ints_msk[5]); + + /* clear all */ + unsigned char ints[5]; + pmu_read_multiple(PCF5063X_REG_INT1, 5, ints); + pmu_read(PCF50635_REG_INT6); + + /* get initial values */ +#if CONFIG_CHARGING + pmu_read_inputs_mbcs(); +#endif + pmu_read_inputs_ooc(); + pmu_read_inputs_gpio(); + + eint_register(&pmu_eint); +} + +/* + * preinit + */ +int pmu_rd_multiple(int address, int count, unsigned char* buffer) +{ + return i2c_rd(0, 0xe6, address, count, buffer); +} + +int pmu_wr_multiple(int address, int count, unsigned char* buffer) +{ + return i2c_wr(0, 0xe6, address, count, buffer); +} + +unsigned char pmu_rd(int address) +{ + unsigned char val; + pmu_rd_multiple(address, 1, &val); + return val; +} + +int pmu_wr(int address, unsigned char val) +{ + return pmu_wr_multiple(address, 1, &val); +} + +void pmu_preinit(void) +{ + static const char init_data[] = + { + /* reset OOC shutdown register */ + PCF5063X_REG_OOCSHDWN, 0x0, + + /* LDO_UNK1: 3000 mV, enabled */ + PCF5063X_REG_LDO1OUT, 0x15, + PCF5063X_REG_LDO1ENA, 0x1, + + /* LDO_UNK2: 3000 mV, enabled */ + PCF5063X_REG_LDO2OUT, 0x15, + PCF5063X_REG_LDO2ENA, 0x1, + + /* LDO_LCD: 3000 mV, enabled */ + PCF5063X_REG_LDO3OUT, 0x15, + PCF5063X_REG_LDO3ENA, 0x1, + + /* LDO_CODEC: 1800 mV, enabled */ + PCF5063X_REG_LDO4OUT, 0x9, + PCF5063X_REG_LDO4ENA, 0x1, + + /* LDO_UNK5: 3000 mV, disabled */ + PCF5063X_REG_LDO5OUT, 0x15, + PCF5063X_REG_LDO5ENA, 0x0, + + /* LDO_CWHEEL: 3000 mV, ON when GPIO2 High */ + PCF5063X_REG_LDO6OUT, 0x15, + PCF5063X_REG_LDO6ENA, 0x4, + + /* LDO_ACCY: 3300 mV, disabled */ + PCF5063X_REG_HCLDOOUT, 0x18, + PCF5063X_REG_HCLDOENA, 0x0, + + /* LDO_CWHEEL is ON in STANDBY state, + LDO_CWHEEL and MEMLDO are ON in UNKNOWN state (TBC) */ + PCF5063X_REG_STBYCTL1, 0x0, + PCF5063X_REG_STBYCTL2, 0x8c, + + /* GPIO1,2 = input, GPIO3 = output High (NoPower default) */ + PCF5063X_REG_GPIOCTL, 0x3, + PCF5063X_REG_GPIO1CFG, 0x0, + PCF5063X_REG_GPIO2CFG, 0x0, + PCF5063X_REG_GPIO3CFG, 0x7, + + /* DOWN2 converter (SDRAM): 1800 mV, enabled, + startup current limit = 15mA*0x10 (TBC) */ + PCF5063X_REG_DOWN2OUT, 0x2f, + PCF5063X_REG_DOWN2ENA, 0x1, + PCF5063X_REG_DOWN2CTL, 0x0, + PCF5063X_REG_DOWN2MXC, 0x10, + + /* MEMLDO: 1800 mV, enabled */ + PCF5063X_REG_MEMLDOOUT, 0x9, + PCF5063X_REG_MEMLDOENA, 0x1, + + /* AUTOLDO (HDD): 3400 mV, disabled, + limit = 1000 mA (40mA*0x19), limit always active */ + PCF5063X_REG_AUTOOUT, 0x6f, +#ifdef BOOTLOADER + PCF5063X_REG_AUTOENA, 0x0, +#endif + PCF5063X_REG_AUTOCTL, 0x0, + PCF5063X_REG_AUTOMXC, 0x59, + + /* Vsysok = 3100 mV */ + PCF5063X_REG_SVMCTL, 0x8, + + /* Reserved */ + 0x58, 0x0, + + /* Mask all PMU interrupts */ + PCF5063X_REG_INT1M, 0xff, + PCF5063X_REG_INT2M, 0xff, + PCF5063X_REG_INT3M, 0xff, + PCF5063X_REG_INT4M, 0xff, + PCF5063X_REG_INT5M, 0xff, + PCF50635_REG_INT6M, 0xff, + + /* Wakeup on rising edge for EXTON1 and EXTON2, + wakeup on falling edge for EXTON3 and !ONKEY, + wakeup on RTC alarm, wakeup on adapter insert, + Vbat status has no effect in state machine */ + PCF5063X_REG_OOCWAKE, 0xdf, + PCF5063X_REG_OOCTIM1, 0xaa, + PCF5063X_REG_OOCTIM2, 0x4a, + PCF5063X_REG_OOCMODE, 0x5, + PCF5063X_REG_OOCCTL, 0x27, + + /* GPO selection = LED external NFET drive signal */ + PCF5063X_REG_GPOCFG, 0x1, + /* LED converter OFF, overvoltage protection enabled, + OCP limit is 500 mA, led_dimstep = 16*0x6/32768 */ +#ifdef BOOTLOADER + PCF5063X_REG_LEDENA, 0x0, +#endif + PCF5063X_REG_LEDCTL, 0x5, + PCF5063X_REG_LEDDIM, 0x6, + + /* end marker */ + 0 + }; + + const char* ptr; + for (ptr = init_data; *ptr != 0; ptr += 2) + pmu_wr(ptr[0], ptr[1]); + + /* clear PMU interrupts */ + unsigned char rd_buf[5]; + pmu_rd_multiple(PCF5063X_REG_INT1, 5, rd_buf); + pmu_rd(PCF50635_REG_INT6); +} diff --git a/firmware/target/arm/s5l8702/ipod6g/pmu-ipod6g.c b/firmware/target/arm/s5l8702/ipod6g/pmu-ipod6g.c deleted file mode 100644 index d282a48d5b..0000000000 --- a/firmware/target/arm/s5l8702/ipod6g/pmu-ipod6g.c +++ /dev/null @@ -1,469 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id: pmu-nano2g.c 27752 2010-08-08 10:49:32Z bertrik $ - * - * Copyright © 2008 Rafaël Carré - * - * 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 "kernel.h" -#include "thread.h" - -#include "pmu-target.h" -#include "adc-target.h" -#include "i2c-s5l8702.h" -#include "gpio-s5l8702.h" - - -int pmu_read_multiple(int address, int count, unsigned char* buffer) -{ - return i2c_read(0, 0xe6, address, count, buffer); -} - -int pmu_write_multiple(int address, int count, unsigned char* buffer) -{ - return i2c_write(0, 0xe6, address, count, buffer); -} - -unsigned char pmu_read(int address) -{ - unsigned char tmp; - - pmu_read_multiple(address, 1, &tmp); - - return tmp; -} - -int pmu_write(int address, unsigned char val) -{ - return pmu_write_multiple(address, 1, &val); -} - -void pmu_ldo_on_in_standby(unsigned int ldo, int onoff) -{ - if (ldo < 4) - { - unsigned char newval = pmu_read(0x3B) & ~(1 << (2 * ldo)); - if (onoff) newval |= 1 << (2 * ldo); - pmu_write(0x3B, newval); - } - else if (ldo < 8) - { - unsigned char newval = pmu_read(0x3C) & ~(1 << (2 * (ldo - 4))); - if (onoff) newval |= 1 << (2 * (ldo - 4)); - pmu_write(0x3C, newval); - } -} - -void pmu_ldo_set_voltage(unsigned int ldo, unsigned char voltage) -{ - if (ldo > 6) return; - pmu_write(0x2d + (ldo << 1), voltage); -} - -void pmu_hdd_power(bool on) -{ - pmu_write(0x1b, on ? 1 : 0); -} - -void pmu_ldo_power_on(unsigned int ldo) -{ - if (ldo > 6) return; - pmu_write(0x2e + (ldo << 1), 1); -} - -void pmu_ldo_power_off(unsigned int ldo) -{ - if (ldo > 6) return; - pmu_write(0x2e + (ldo << 1), 0); -} - -void pmu_set_wake_condition(unsigned char condition) -{ - pmu_write(0xd, condition); -} - -void pmu_enter_standby(void) -{ - pmu_write(0xc, 1); -} - -void pmu_read_rtc(unsigned char* buffer) -{ - pmu_read_multiple(0x59, 7, buffer); -} - -void pmu_write_rtc(unsigned char* buffer) -{ - pmu_write_multiple(0x59, 7, buffer); -} - -/* - * ADC - */ -#define ADC_FULL_SCALE 2000 -#define ADC_FULL_SCALE_VISA 2400 -#define ADC_SUBTR_OFFSET 2250 - -static struct mutex pmu_adc_mutex; - -/* converts raw 8/10-bit value to millivolts */ -unsigned short pmu_adc_raw2mv( - const struct pmu_adc_channel *ch, unsigned short raw) -{ - int full_scale = ADC_FULL_SCALE; - int offset = 0; - - switch (ch->adcc1 & PCF5063X_ADCC1_ADCMUX_MASK) - { - case PCF5063X_ADCC1_MUX_BATSNS_RES: - case PCF5063X_ADCC1_MUX_ADCIN2_RES: - full_scale *= ((ch->adcc1 & PCF5063X_ADCC3_RES_DIV_MASK) == - PCF5063X_ADCC3_RES_DIV_TWO) ? 2 : 3; - break; - case PCF5063X_ADCC1_MUX_BATSNS_SUBTR: - case PCF5063X_ADCC1_MUX_ADCIN2_SUBTR: - offset = ADC_SUBTR_OFFSET; - break; - case PCF5063X_ADCC1_MUX_BATTEMP: - if (ch->adcc2 & PCF5063X_ADCC2_RATIO_BATTEMP) - full_scale = ADC_FULL_SCALE_VISA; - break; - case PCF5063X_ADCC1_MUX_ADCIN1: - if (ch->adcc2 & PCF5063X_ADCC2_RATIO_ADCIN1) - full_scale = ADC_FULL_SCALE_VISA; - break; - } - - int nrb = ((ch->adcc1 & PCF5063X_ADCC1_RES_MASK) == - PCF5063X_ADCC1_RES_8BIT) ? 8 : 10; - return (raw * full_scale / ((1<adcc3); - if (ch->bias_dly) - sleep(ch->bias_dly); - uint8_t buf[2] = { ch->adcc2, ch->adcc1 | PCF5063X_ADCC1_ADCSTART }; - pmu_write_multiple(PCF5063X_REG_ADCC2, 2, buf); - - int adcs3 = 0; - while (!(adcs3 & PCF5063X_ADCS3_ADCRDY)) - { - yield(); - adcs3 = pmu_read(PCF5063X_REG_ADCS3); - } - - int raw = pmu_read(PCF5063X_REG_ADCS1); - if ((ch->adcc1 & PCF5063X_ADCC1_RES_MASK) == PCF5063X_ADCC1_RES_10BIT) - raw = (raw << 2) | (adcs3 & PCF5063X_ADCS3_ADCDAT1L_MASK); - - mutex_unlock(&pmu_adc_mutex); - return raw; -} - -/* - * eINT - */ -#define Q_EINT 0 - -static char pmu_thread_stack[DEFAULT_STACK_SIZE/2]; -static struct event_queue pmu_queue; -static unsigned char ints_msk[6]; - -static void pmu_eint_isr(struct eint_handler*); - -static struct eint_handler pmu_eint = -{ - .gpio_n = GPIO_EINT_PMU, - .type = EIC_INTTYPE_LEVEL, - .level = EIC_INTLEVEL_LOW, - .isr = pmu_eint_isr, -}; - -static int pmu_input_holdswitch; - -int pmu_holdswitch_locked(void) -{ - return pmu_input_holdswitch; -} - -#ifdef IPOD_ACCESSORY_PROTOCOL -static int pmu_input_accessory; - -int pmu_accessory_present(void) -{ - return pmu_input_accessory; -} -#endif - -#if CONFIG_CHARGING -static int pmu_input_firewire; - -int pmu_firewire_present(void) -{ - return pmu_input_firewire; -} - -static void pmu_read_inputs_mbcs(void) -{ - pmu_input_firewire = !!(pmu_read(PCF5063X_REG_MBCS1) - & PCF5063X_MBCS1_ADAPTPRES); -} -#endif - -static void pmu_read_inputs_gpio(void) -{ - pmu_input_holdswitch = !(pmu_read(PCF50635_REG_GPIOSTAT) - & PCF50635_GPIOSTAT_GPIO2); -} - -static void pmu_read_inputs_ooc(void) -{ - unsigned char oocstat = pmu_read(PCF5063X_REG_OOCSTAT); - if (oocstat & PCF5063X_OOCSTAT_EXTON2) - usb_insert_int(); - else - usb_remove_int(); -#ifdef IPOD_ACCESSORY_PROTOCOL - pmu_input_accessory = !(oocstat & PCF5063X_OOCSTAT_EXTON3); -#endif -} - -static void pmu_eint_isr(struct eint_handler *h) -{ - eint_unregister(h); - queue_post(&pmu_queue, Q_EINT, 0); -} - -static void NORETURN_ATTR pmu_thread(void) -{ - struct queue_event ev; - unsigned char ints[6]; - - while (true) - { - queue_wait_w_tmo(&pmu_queue, &ev, TIMEOUT_BLOCK); - switch (ev.id) - { - case Q_EINT: - /* read (clear) PMU interrupts, this will also - raise the PMU IRQ pin */ - pmu_read_multiple(PCF5063X_REG_INT1, 2, ints); - ints[5] = pmu_read(PCF50635_REG_INT6); - -#if CONFIG_CHARGING - if (ints[0] & ~ints_msk[0]) pmu_read_inputs_mbcs(); -#endif - if (ints[1] & ~ints_msk[1]) pmu_read_inputs_ooc(); - if (ints[5] & ~ints_msk[5]) pmu_read_inputs_gpio(); - - eint_register(&pmu_eint); - break; - - case SYS_TIMEOUT: - break; - } - } -} - -/* main init */ -void pmu_init(void) -{ - mutex_init(&pmu_adc_mutex); - queue_init(&pmu_queue, false); - - create_thread(pmu_thread, - pmu_thread_stack, sizeof(pmu_thread_stack), 0, - "PMU" IF_PRIO(, PRIORITY_SYSTEM) IF_COP(, CPU)); - - /* configure PMU interrutps */ - for (int i = 0; i < 6; i++) - ints_msk[i] = 0xff; - -#if CONFIG_CHARGING - ints_msk[0] &= ~PCF5063X_INT1_ADPINS & /* FireWire */ - ~PCF5063X_INT1_ADPREM; -#endif - ints_msk[1] &= ~PCF5063X_INT2_EXTON2R & /* USB */ - ~PCF5063X_INT2_EXTON2F; -#ifdef IPOD_ACCESSORY_PROTOCOL - ints_msk[1] &= ~PCF5063X_INT2_EXTON3R & /* Accessory */ - ~PCF5063X_INT2_EXTON3F; -#endif - ints_msk[5] &= ~PCF50635_INT6_GPIO2; /* Holdswitch */ - - pmu_write_multiple(PCF5063X_REG_INT1M, 5, ints_msk); - pmu_write(PCF50635_REG_INT6M, ints_msk[5]); - - /* clear all */ - unsigned char ints[5]; - pmu_read_multiple(PCF5063X_REG_INT1, 5, ints); - pmu_read(PCF50635_REG_INT6); - - /* get initial values */ -#if CONFIG_CHARGING - pmu_read_inputs_mbcs(); -#endif - pmu_read_inputs_ooc(); - pmu_read_inputs_gpio(); - - eint_register(&pmu_eint); -} - -/* - * preinit - */ -int pmu_rd_multiple(int address, int count, unsigned char* buffer) -{ - return i2c_rd(0, 0xe6, address, count, buffer); -} - -int pmu_wr_multiple(int address, int count, unsigned char* buffer) -{ - return i2c_wr(0, 0xe6, address, count, buffer); -} - -unsigned char pmu_rd(int address) -{ - unsigned char val; - pmu_rd_multiple(address, 1, &val); - return val; -} - -int pmu_wr(int address, unsigned char val) -{ - return pmu_wr_multiple(address, 1, &val); -} - -void pmu_preinit(void) -{ - static const char init_data[] = - { - /* reset OOC shutdown register */ - PCF5063X_REG_OOCSHDWN, 0x0, - - /* LDO_UNK1: 3000 mV, enabled */ - PCF5063X_REG_LDO1OUT, 0x15, - PCF5063X_REG_LDO1ENA, 0x1, - - /* LDO_UNK2: 3000 mV, enabled */ - PCF5063X_REG_LDO2OUT, 0x15, - PCF5063X_REG_LDO2ENA, 0x1, - - /* LDO_LCD: 3000 mV, enabled */ - PCF5063X_REG_LDO3OUT, 0x15, - PCF5063X_REG_LDO3ENA, 0x1, - - /* LDO_CODEC: 1800 mV, enabled */ - PCF5063X_REG_LDO4OUT, 0x9, - PCF5063X_REG_LDO4ENA, 0x1, - - /* LDO_UNK5: 3000 mV, disabled */ - PCF5063X_REG_LDO5OUT, 0x15, - PCF5063X_REG_LDO5ENA, 0x0, - - /* LDO_CWHEEL: 3000 mV, ON when GPIO2 High */ - PCF5063X_REG_LDO6OUT, 0x15, - PCF5063X_REG_LDO6ENA, 0x4, - - /* LDO_ACCY: 3300 mV, disabled */ - PCF5063X_REG_HCLDOOUT, 0x18, - PCF5063X_REG_HCLDOENA, 0x0, - - /* LDO_CWHEEL is ON in STANDBY state, - LDO_CWHEEL and MEMLDO are ON in UNKNOWN state (TBC) */ - PCF5063X_REG_STBYCTL1, 0x0, - PCF5063X_REG_STBYCTL2, 0x8c, - - /* GPIO1,2 = input, GPIO3 = output High (NoPower default) */ - PCF5063X_REG_GPIOCTL, 0x3, - PCF5063X_REG_GPIO1CFG, 0x0, - PCF5063X_REG_GPIO2CFG, 0x0, - PCF5063X_REG_GPIO3CFG, 0x7, - - /* DOWN2 converter (SDRAM): 1800 mV, enabled, - startup current limit = 15mA*0x10 (TBC) */ - PCF5063X_REG_DOWN2OUT, 0x2f, - PCF5063X_REG_DOWN2ENA, 0x1, - PCF5063X_REG_DOWN2CTL, 0x0, - PCF5063X_REG_DOWN2MXC, 0x10, - - /* MEMLDO: 1800 mV, enabled */ - PCF5063X_REG_MEMLDOOUT, 0x9, - PCF5063X_REG_MEMLDOENA, 0x1, - - /* AUTOLDO (HDD): 3400 mV, disabled, - limit = 1000 mA (40mA*0x19), limit always active */ - PCF5063X_REG_AUTOOUT, 0x6f, -#ifdef BOOTLOADER - PCF5063X_REG_AUTOENA, 0x0, -#endif - PCF5063X_REG_AUTOCTL, 0x0, - PCF5063X_REG_AUTOMXC, 0x59, - - /* Vsysok = 3100 mV */ - PCF5063X_REG_SVMCTL, 0x8, - - /* Reserved */ - 0x58, 0x0, - - /* Mask all PMU interrupts */ - PCF5063X_REG_INT1M, 0xff, - PCF5063X_REG_INT2M, 0xff, - PCF5063X_REG_INT3M, 0xff, - PCF5063X_REG_INT4M, 0xff, - PCF5063X_REG_INT5M, 0xff, - PCF50635_REG_INT6M, 0xff, - - /* Wakeup on rising edge for EXTON1 and EXTON2, - wakeup on falling edge for EXTON3 and !ONKEY, - wakeup on RTC alarm, wakeup on adapter insert, - Vbat status has no effect in state machine */ - PCF5063X_REG_OOCWAKE, 0xdf, - PCF5063X_REG_OOCTIM1, 0xaa, - PCF5063X_REG_OOCTIM2, 0x4a, - PCF5063X_REG_OOCMODE, 0x5, - PCF5063X_REG_OOCCTL, 0x27, - - /* GPO selection = LED external NFET drive signal */ - PCF5063X_REG_GPOCFG, 0x1, - /* LED converter OFF, overvoltage protection enabled, - OCP limit is 500 mA, led_dimstep = 16*0x6/32768 */ -#ifdef BOOTLOADER - PCF5063X_REG_LEDENA, 0x0, -#endif - PCF5063X_REG_LEDCTL, 0x5, - PCF5063X_REG_LEDDIM, 0x6, - - /* end marker */ - 0 - }; - - const char* ptr; - for (ptr = init_data; *ptr != 0; ptr += 2) - pmu_wr(ptr[0], ptr[1]); - - /* clear PMU interrupts */ - unsigned char rd_buf[5]; - pmu_rd_multiple(PCF5063X_REG_INT1, 5, rd_buf); - pmu_rd(PCF50635_REG_INT6); -} diff --git a/firmware/target/arm/s5l8702/ipod6g/power-6g.c b/firmware/target/arm/s5l8702/ipod6g/power-6g.c new file mode 100644 index 0000000000..1a8e1e1a7e --- /dev/null +++ b/firmware/target/arm/s5l8702/ipod6g/power-6g.c @@ -0,0 +1,134 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: power-nano2g.c 28190 2010-10-01 18:09:10Z Buschel $ + * + * Copyright © 2009 Bertrik Sikken + * + * 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 "config.h" +#include "inttypes.h" +#include "s5l8702.h" +#include "power.h" +#include "panic.h" +#include "pmu-target.h" +#include "usb_core.h" /* for usb_charging_maxcurrent_change */ + +static int idepowered; + +void power_off(void) +{ + /* USB inserted or EXTON1 */ + pmu_set_wake_condition( + PCF5063X_OOCWAKE_EXTON2 | PCF5063X_OOCWAKE_EXTON1); + pmu_enter_standby(); + + while(1); +} + +void power_init(void) +{ + pmu_init(); + + idepowered = false; + + /* DOWN1CTL: CPU DVM step time = 30us (default: no DVM) */ + pmu_write(0x20, 2); + + /* USB power configuration: + * + * GPIO C0 is probably related to the LTC4066's CLPROG + * pin (see datasheet). Setting it high allows to double + * the maximum current selected by HPWR: + * + * GPIO B6 GPIO C0 USB current + * HPWR CLPROG ??? limit (mA) + * ------- ---------- ----------- + * 0 0 100 + * 1 0 500 + * 0 1 200 + * 1 1 1000 ??? (max.seen ~750mA) + * + * USB current limit includes battery charge and device + * consumption. Battery charge has it's own limit at + * 330~340 mA (configured using RPROG). + * + * Setting either of GPIO C1 or GPIO C2 disables battery + * charge, power needed for device consumptiom is drained + * from USB or AC adaptor when present. If external power + * is not present or it is insufficient or limited, + * additional required power is drained from battery. + */ + PCONB = (PCONB & 0x000000ff) + | (0xe << 8) /* route D+ to ADC2: off */ + | (0xe << 12) /* route D- to ADC2: off */ + | (0x0 << 16) /* USB related input, POL pin ??? */ + | (0x0 << 20) /* USB related input, !CHRG pin ??? */ + | (0xe << 24) /* HPWR: 100mA */ + | (0xe << 28); /* USB suspend: off */ + + PCONC = (PCONC & 0xffff0000) + | (0xe << 0) /* double HPWR limit: off */ + | (0xe << 4) /* disable battery charge: off */ + | (0xe << 8) /* disable battery charge: off */ + | (0x0 << 12); /* USB inserted/not inserted */ +} + +void ide_power_enable(bool on) +{ + idepowered = on; + pmu_hdd_power(on); +} + +bool ide_powered() +{ + return idepowered; +} + +#if CONFIG_CHARGING + +#ifdef HAVE_USB_CHARGING_ENABLE +void usb_charging_maxcurrent_change(int maxcurrent) +{ + bool suspend_charging = (maxcurrent < 100); + bool fast_charging = (maxcurrent >= 500); + + /* This GPIO is connected to the LTC4066's SUSP pin */ + /* Setting it high prevents any power being drawn over USB */ + /* which supports USB suspend */ + GPIOCMD = 0xb070e | (suspend_charging ? 1 : 0); + + /* This GPIO is connected to the LTC4066's HPWR pin */ + /* Setting it low limits current to 100mA, setting it high allows 500mA */ + GPIOCMD = 0xb060e | (fast_charging ? 1 : 0); +} +#endif + +unsigned int power_input_status(void) +{ + unsigned int status = POWER_INPUT_NONE; + if (usb_detect() == USB_INSERTED) + status |= POWER_INPUT_USB_CHARGER; + if (pmu_firewire_present()) + status |= POWER_INPUT_MAIN_CHARGER; + return status; +} + +bool charging_state(void) +{ + return (PDAT(11) & 0x10) ? 0 : 1; +} +#endif /* CONFIG_CHARGING */ diff --git a/firmware/target/arm/s5l8702/ipod6g/power-ipod6g.c b/firmware/target/arm/s5l8702/ipod6g/power-ipod6g.c deleted file mode 100644 index 1a8e1e1a7e..0000000000 --- a/firmware/target/arm/s5l8702/ipod6g/power-ipod6g.c +++ /dev/null @@ -1,134 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id: power-nano2g.c 28190 2010-10-01 18:09:10Z Buschel $ - * - * Copyright © 2009 Bertrik Sikken - * - * 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 "config.h" -#include "inttypes.h" -#include "s5l8702.h" -#include "power.h" -#include "panic.h" -#include "pmu-target.h" -#include "usb_core.h" /* for usb_charging_maxcurrent_change */ - -static int idepowered; - -void power_off(void) -{ - /* USB inserted or EXTON1 */ - pmu_set_wake_condition( - PCF5063X_OOCWAKE_EXTON2 | PCF5063X_OOCWAKE_EXTON1); - pmu_enter_standby(); - - while(1); -} - -void power_init(void) -{ - pmu_init(); - - idepowered = false; - - /* DOWN1CTL: CPU DVM step time = 30us (default: no DVM) */ - pmu_write(0x20, 2); - - /* USB power configuration: - * - * GPIO C0 is probably related to the LTC4066's CLPROG - * pin (see datasheet). Setting it high allows to double - * the maximum current selected by HPWR: - * - * GPIO B6 GPIO C0 USB current - * HPWR CLPROG ??? limit (mA) - * ------- ---------- ----------- - * 0 0 100 - * 1 0 500 - * 0 1 200 - * 1 1 1000 ??? (max.seen ~750mA) - * - * USB current limit includes battery charge and device - * consumption. Battery charge has it's own limit at - * 330~340 mA (configured using RPROG). - * - * Setting either of GPIO C1 or GPIO C2 disables battery - * charge, power needed for device consumptiom is drained - * from USB or AC adaptor when present. If external power - * is not present or it is insufficient or limited, - * additional required power is drained from battery. - */ - PCONB = (PCONB & 0x000000ff) - | (0xe << 8) /* route D+ to ADC2: off */ - | (0xe << 12) /* route D- to ADC2: off */ - | (0x0 << 16) /* USB related input, POL pin ??? */ - | (0x0 << 20) /* USB related input, !CHRG pin ??? */ - | (0xe << 24) /* HPWR: 100mA */ - | (0xe << 28); /* USB suspend: off */ - - PCONC = (PCONC & 0xffff0000) - | (0xe << 0) /* double HPWR limit: off */ - | (0xe << 4) /* disable battery charge: off */ - | (0xe << 8) /* disable battery charge: off */ - | (0x0 << 12); /* USB inserted/not inserted */ -} - -void ide_power_enable(bool on) -{ - idepowered = on; - pmu_hdd_power(on); -} - -bool ide_powered() -{ - return idepowered; -} - -#if CONFIG_CHARGING - -#ifdef HAVE_USB_CHARGING_ENABLE -void usb_charging_maxcurrent_change(int maxcurrent) -{ - bool suspend_charging = (maxcurrent < 100); - bool fast_charging = (maxcurrent >= 500); - - /* This GPIO is connected to the LTC4066's SUSP pin */ - /* Setting it high prevents any power being drawn over USB */ - /* which supports USB suspend */ - GPIOCMD = 0xb070e | (suspend_charging ? 1 : 0); - - /* This GPIO is connected to the LTC4066's HPWR pin */ - /* Setting it low limits current to 100mA, setting it high allows 500mA */ - GPIOCMD = 0xb060e | (fast_charging ? 1 : 0); -} -#endif - -unsigned int power_input_status(void) -{ - unsigned int status = POWER_INPUT_NONE; - if (usb_detect() == USB_INSERTED) - status |= POWER_INPUT_USB_CHARGER; - if (pmu_firewire_present()) - status |= POWER_INPUT_MAIN_CHARGER; - return status; -} - -bool charging_state(void) -{ - return (PDAT(11) & 0x10) ? 0 : 1; -} -#endif /* CONFIG_CHARGING */ diff --git a/firmware/target/arm/s5l8702/ipod6g/powermgmt-6g.c b/firmware/target/arm/s5l8702/ipod6g/powermgmt-6g.c new file mode 100644 index 0000000000..c5f9c9b9f5 --- /dev/null +++ b/firmware/target/arm/s5l8702/ipod6g/powermgmt-6g.c @@ -0,0 +1,81 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: powermgmt-nano2g.c 28159 2010-09-24 22:42:06Z Buschel $ + * + * Copyright © 2008 Rafaël Carré + * + * 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 "powermgmt.h" +#include "pmu-target.h" +#include "power.h" +#include "audiohw.h" +#include "adc-target.h" + +const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] = +{ + 3500 +}; + +const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] = +{ + 3300 +}; + +/* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */ +const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11] = +{ + { 3500, 3670, 3720, 3750, 3770, 3800, 3860, 3920, 3980, 4070, 4170 } +}; + +#if CONFIG_CHARGING +/* voltages (millivolt) of 0%, 10%, ... 100% when charging enabled */ +const unsigned short percent_to_volt_charge[11] = +{ + 3700, 3820, 3900, 3950, 3990, 4030, 4070, 4120, 4170, 4190, 4200 +}; +#endif /* CONFIG_CHARGING */ + +/* Returns battery voltage from ADC [millivolts] */ +int _battery_voltage(void) +{ + return adc_read_battery_voltage(); +} + +#ifdef HAVE_ACCESSORY_SUPPLY +void accessory_supply_set(bool enable) +{ + if (enable) + { + /* Accessory voltage supply on */ + pmu_ldo_power_on(6); + } + else + { + /* Accessory voltage supply off */ + pmu_ldo_power_off(6); + } +} +#endif + +#ifdef HAVE_LINEOUT_POWEROFF +void lineout_set(bool enable) +{ + /* Call audio hardware driver implementation */ + audiohw_enable_lineout(enable); +} +#endif diff --git a/firmware/target/arm/s5l8702/ipod6g/powermgmt-ipod6g.c b/firmware/target/arm/s5l8702/ipod6g/powermgmt-ipod6g.c deleted file mode 100644 index c5f9c9b9f5..0000000000 --- a/firmware/target/arm/s5l8702/ipod6g/powermgmt-ipod6g.c +++ /dev/null @@ -1,81 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id: powermgmt-nano2g.c 28159 2010-09-24 22:42:06Z Buschel $ - * - * Copyright © 2008 Rafaël Carré - * - * 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 "powermgmt.h" -#include "pmu-target.h" -#include "power.h" -#include "audiohw.h" -#include "adc-target.h" - -const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] = -{ - 3500 -}; - -const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] = -{ - 3300 -}; - -/* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */ -const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11] = -{ - { 3500, 3670, 3720, 3750, 3770, 3800, 3860, 3920, 3980, 4070, 4170 } -}; - -#if CONFIG_CHARGING -/* voltages (millivolt) of 0%, 10%, ... 100% when charging enabled */ -const unsigned short percent_to_volt_charge[11] = -{ - 3700, 3820, 3900, 3950, 3990, 4030, 4070, 4120, 4170, 4190, 4200 -}; -#endif /* CONFIG_CHARGING */ - -/* Returns battery voltage from ADC [millivolts] */ -int _battery_voltage(void) -{ - return adc_read_battery_voltage(); -} - -#ifdef HAVE_ACCESSORY_SUPPLY -void accessory_supply_set(bool enable) -{ - if (enable) - { - /* Accessory voltage supply on */ - pmu_ldo_power_on(6); - } - else - { - /* Accessory voltage supply off */ - pmu_ldo_power_off(6); - } -} -#endif - -#ifdef HAVE_LINEOUT_POWEROFF -void lineout_set(bool enable) -{ - /* Call audio hardware driver implementation */ - audiohw_enable_lineout(enable); -} -#endif diff --git a/firmware/target/arm/s5l8702/ipod6g/rtc-6g.c b/firmware/target/arm/s5l8702/ipod6g/rtc-6g.c new file mode 100644 index 0000000000..384cded758 --- /dev/null +++ b/firmware/target/arm/s5l8702/ipod6g/rtc-6g.c @@ -0,0 +1,75 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: rtc-nano2g.c 23114 2009-10-11 18:20:56Z theseven $ + * + * Copyright (C) 2002 by Linus Nielsen Feltzing, Uwe Freese, Laurent Baum + * + * 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 "rtc.h" +#include "kernel.h" +#include "system.h" +#include "pmu-target.h" +#include "timefuncs.h" + +void rtc_init(void) +{ +} + +int rtc_read_datetime(struct tm *tm) +{ + unsigned int i; + unsigned char buf[7]; + + pmu_read_rtc(buf); + + for (i = 0; i < sizeof(buf); i++) + buf[i] = BCD2DEC(buf[i]); + + tm->tm_sec = buf[0]; + tm->tm_min = buf[1]; + tm->tm_hour = buf[2]; + tm->tm_mday = buf[4]; + tm->tm_mon = buf[5] - 1; + tm->tm_year = buf[6] + 100; + tm->tm_yday = 0; /* Not implemented for now */ + + set_day_of_week(tm); + + return 0; +} + +int rtc_write_datetime(const struct tm *tm) +{ + unsigned int i; + unsigned char buf[7]; + + buf[0] = tm->tm_sec; + buf[1] = tm->tm_min; + buf[2] = tm->tm_hour; + buf[3] = tm->tm_wday; + buf[4] = tm->tm_mday; + buf[5] = tm->tm_mon + 1; + buf[6] = tm->tm_year - 100; + + for (i = 0; i < sizeof(buf); i++) + buf[i] = DEC2BCD(buf[i]); + + pmu_write_rtc(buf); + + return 0; +} + diff --git a/firmware/target/arm/s5l8702/ipod6g/rtc-ipod6g.c b/firmware/target/arm/s5l8702/ipod6g/rtc-ipod6g.c deleted file mode 100644 index 384cded758..0000000000 --- a/firmware/target/arm/s5l8702/ipod6g/rtc-ipod6g.c +++ /dev/null @@ -1,75 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id: rtc-nano2g.c 23114 2009-10-11 18:20:56Z theseven $ - * - * Copyright (C) 2002 by Linus Nielsen Feltzing, Uwe Freese, Laurent Baum - * - * 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 "rtc.h" -#include "kernel.h" -#include "system.h" -#include "pmu-target.h" -#include "timefuncs.h" - -void rtc_init(void) -{ -} - -int rtc_read_datetime(struct tm *tm) -{ - unsigned int i; - unsigned char buf[7]; - - pmu_read_rtc(buf); - - for (i = 0; i < sizeof(buf); i++) - buf[i] = BCD2DEC(buf[i]); - - tm->tm_sec = buf[0]; - tm->tm_min = buf[1]; - tm->tm_hour = buf[2]; - tm->tm_mday = buf[4]; - tm->tm_mon = buf[5] - 1; - tm->tm_year = buf[6] + 100; - tm->tm_yday = 0; /* Not implemented for now */ - - set_day_of_week(tm); - - return 0; -} - -int rtc_write_datetime(const struct tm *tm) -{ - unsigned int i; - unsigned char buf[7]; - - buf[0] = tm->tm_sec; - buf[1] = tm->tm_min; - buf[2] = tm->tm_hour; - buf[3] = tm->tm_wday; - buf[4] = tm->tm_mday; - buf[5] = tm->tm_mon + 1; - buf[6] = tm->tm_year - 100; - - for (i = 0; i < sizeof(buf); i++) - buf[i] = DEC2BCD(buf[i]); - - pmu_write_rtc(buf); - - return 0; -} - diff --git a/firmware/target/arm/s5l8702/ipod6g/serial-6g.c b/firmware/target/arm/s5l8702/ipod6g/serial-6g.c new file mode 100644 index 0000000000..f1f06987bc --- /dev/null +++ b/firmware/target/arm/s5l8702/ipod6g/serial-6g.c @@ -0,0 +1,249 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2014 by Cástor Muñoz + * + * 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 "config.h" +#include "cpu.h" +#include "system.h" +#include "serial.h" + +#include "s5l8702.h" +#include "uc870x.h" + +/* Define LOGF_ENABLE to enable logf output in this file */ +#define LOGF_ENABLE +#include "logf.h" + + +/* shall include serial HW configuracion for specific target */ +#define IPOD6G_UART_CLK_HZ 12000000 /* external OSC0 ??? */ + +/* This values below are valid with a UCLK of 12MHz */ +#define BRDATA_9600 (77) /* 9615 */ +#define BRDATA_19200 (38) /* 19231 */ +#define BRDATA_28800 (25) /* 28846 */ +#define BRDATA_38400 (19 | (0xc330c << 8)) /* 38305 */ +#define BRDATA_57600 (12) /* 57692 */ +#define BRDATA_115200 (6 | (0xffffff << 8)) /* 114286 */ + + +extern const struct uartc s5l8702_uartc; +#ifdef IPOD_ACCESSORY_PROTOCOL +static void iap_rx_isr(int, char*, char*, uint32_t); +#endif + +struct uartc_port ser_port IDATA_ATTR = +{ + /* location */ + .uartc = &s5l8702_uartc, + .id = 0, + + /* configuration */ + .rx_trg = UFCON_RX_FIFO_TRG_4, + .tx_trg = UFCON_TX_FIFO_TRG_EMPTY, + .clksel = UCON_CLKSEL_ECLK, + .clkhz = IPOD6G_UART_CLK_HZ, + + /* interrupt callbacks */ +#ifdef IPOD_ACCESSORY_PROTOCOL + .rx_cb = iap_rx_isr, +#else + .rx_cb = NULL, +#endif + .tx_cb = NULL, /* polling */ +}; + +/* + * serial driver API + */ +int tx_rdy(void) +{ + return uartc_port_tx_ready(&ser_port) ? 1 : 0; +} + +void tx_writec(unsigned char c) +{ + uartc_port_tx_byte(&ser_port, c); +} + +#ifndef IPOD_ACCESSORY_PROTOCOL +void serial_setup(void) +{ + uartc_port_open(&ser_port); + + /* set a default configuration, Tx and Rx modes are + disabled when the port is initialized */ + uartc_port_config(&ser_port, ULCON_DATA_BITS_8, + ULCON_PARITY_NONE, ULCON_STOP_BITS_1); + uartc_port_set_bitrate_raw(&ser_port, BRDATA_115200); + + /* enable Tx interrupt request or POLLING mode */ + uartc_port_set_tx_mode(&ser_port, UCON_MODE_INTREQ); + + logf("[%lu] "MODEL_NAME" port %d ready!", USEC_TIMER, ser_port.id); +} + + +#else /* IPOD_ACCESSORY_PROTOCOL */ +#include "kernel.h" +#include "pmu-target.h" +#include "iap.h" + +static enum { + ABR_STATUS_LAUNCHED, /* ST_SYNC */ + ABR_STATUS_SYNCING, /* ST_SOF */ + ABR_STATUS_DONE +} abr_status; + +static int bitrate = 0; +static bool acc_plugged = false; + +static void serial_acc_tick(void) +{ + bool plugged = pmu_accessory_present(); + if (acc_plugged != plugged) + { + acc_plugged = plugged; + if (acc_plugged) + { + uartc_open(ser_port.uartc); + uartc_port_open(&ser_port); + /* set a default configuration, Tx and Rx modes are + disabled when the port is initialized */ + uartc_port_config(&ser_port, ULCON_DATA_BITS_8, + ULCON_PARITY_NONE, ULCON_STOP_BITS_1); + uartc_port_set_tx_mode(&ser_port, UCON_MODE_INTREQ); + serial_bitrate(bitrate); + } + else + { + uartc_port_close(&ser_port); + uartc_close(ser_port.uartc); + } + } +} + +void serial_setup(void) +{ + uartc_close(ser_port.uartc); + tick_add_task(serial_acc_tick); +} + +void serial_bitrate(int rate) +{ + bitrate = rate; + if (!acc_plugged) + return; + + logf("[%lu] serial_bitrate(%d)", USEC_TIMER, rate); + + if (rate == 0) { + /* Using auto-bitrate (ABR) to detect accessory Tx speed: + * + * + Here: + * - Disable Rx logic to clean the FIFO and the shift + * register, thus no Rx data interrupts are generated. + * - Launch ABR and wait for a low pulse in Rx line. + * + * + In ISR, when a low pulse is detected (ideally it is the + * start bit of 0xff): + * - Calculate and configure detected speed. + * - Enable Rx to verify that the next received data frame + * is 0x55 or 0xff: + * - If so, it's assumed bit rate is correctly detected, + * it will not be modified until speed is changed using + * RB options menu. + * - If not, reset iAP state machine and launch a new ABR. + */ + uartc_port_set_rx_mode(&ser_port, UCON_MODE_DISABLED); + uartc_port_abr_start(&ser_port); + abr_status = ABR_STATUS_LAUNCHED; + } + else { + uint32_t brdata; + if (rate == 57600) brdata = BRDATA_57600; + else if (rate == 38400) brdata = BRDATA_38400; + else if (rate == 19200) brdata = BRDATA_19200; + else brdata = BRDATA_9600; + uartc_port_abr_stop(&ser_port); /* abort ABR if already launched */ + uartc_port_set_bitrate_raw(&ser_port, brdata); + uartc_port_set_rx_mode(&ser_port, UCON_MODE_INTREQ); + abr_status = ABR_STATUS_DONE; + } +} + +static void iap_rx_isr(int len, char *data, char *err, uint32_t abr_cnt) +{ + /* ignore Rx errors, upper layer will discard bad packets */ + (void) err; + + static int sync_retry; + + if (abr_status == ABR_STATUS_LAUNCHED) { + /* autobauding */ + if (abr_cnt) { + #define BR2CNT(s) (IPOD6G_UART_CLK_HZ / (unsigned)(s)) + if (abr_cnt < BR2CNT(57600*1.1) || abr_cnt > BR2CNT(9600*0.9)) { + /* detected speed out of range, relaunch ABR */ + uartc_port_abr_start(&ser_port); + return; + } + /* valid speed detected, select it */ + uint32_t brdata; + if (abr_cnt < BR2CNT(48000)) brdata = BRDATA_57600; + else if (abr_cnt < BR2CNT(33600)) brdata = BRDATA_38400; + else if (abr_cnt < BR2CNT(24000)) brdata = BRDATA_28800; + else if (abr_cnt < BR2CNT(14400)) brdata = BRDATA_19200; + else brdata = BRDATA_9600; + + /* set detected speed */ + uartc_port_set_bitrate_raw(&ser_port, brdata); + uartc_port_set_rx_mode(&ser_port, UCON_MODE_INTREQ); + + /* enter SOF state */ + iap_getc(0xff); + + abr_status = ABR_STATUS_SYNCING; + sync_retry = 2; /* we are expecting [0xff] 0x55 */ + } + } + + /* process received data */ + while (len--) + { + bool sync_done = !iap_getc(*data++); + + if (abr_status == ABR_STATUS_SYNCING) + { + if (sync_done) { + abr_status = ABR_STATUS_DONE; + } + else if (--sync_retry == 0) { + /* invalid speed detected, relaunch ABR + discarding remaining data (if any) */ + serial_bitrate(0); + break; + } + } + } +} +#endif /* IPOD_ACCESSORY_PROTOCOL */ diff --git a/firmware/target/arm/s5l8702/ipod6g/serial-ipod6g.c b/firmware/target/arm/s5l8702/ipod6g/serial-ipod6g.c deleted file mode 100644 index f1f06987bc..0000000000 --- a/firmware/target/arm/s5l8702/ipod6g/serial-ipod6g.c +++ /dev/null @@ -1,249 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2014 by Cástor Muñoz - * - * 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 "config.h" -#include "cpu.h" -#include "system.h" -#include "serial.h" - -#include "s5l8702.h" -#include "uc870x.h" - -/* Define LOGF_ENABLE to enable logf output in this file */ -#define LOGF_ENABLE -#include "logf.h" - - -/* shall include serial HW configuracion for specific target */ -#define IPOD6G_UART_CLK_HZ 12000000 /* external OSC0 ??? */ - -/* This values below are valid with a UCLK of 12MHz */ -#define BRDATA_9600 (77) /* 9615 */ -#define BRDATA_19200 (38) /* 19231 */ -#define BRDATA_28800 (25) /* 28846 */ -#define BRDATA_38400 (19 | (0xc330c << 8)) /* 38305 */ -#define BRDATA_57600 (12) /* 57692 */ -#define BRDATA_115200 (6 | (0xffffff << 8)) /* 114286 */ - - -extern const struct uartc s5l8702_uartc; -#ifdef IPOD_ACCESSORY_PROTOCOL -static void iap_rx_isr(int, char*, char*, uint32_t); -#endif - -struct uartc_port ser_port IDATA_ATTR = -{ - /* location */ - .uartc = &s5l8702_uartc, - .id = 0, - - /* configuration */ - .rx_trg = UFCON_RX_FIFO_TRG_4, - .tx_trg = UFCON_TX_FIFO_TRG_EMPTY, - .clksel = UCON_CLKSEL_ECLK, - .clkhz = IPOD6G_UART_CLK_HZ, - - /* interrupt callbacks */ -#ifdef IPOD_ACCESSORY_PROTOCOL - .rx_cb = iap_rx_isr, -#else - .rx_cb = NULL, -#endif - .tx_cb = NULL, /* polling */ -}; - -/* - * serial driver API - */ -int tx_rdy(void) -{ - return uartc_port_tx_ready(&ser_port) ? 1 : 0; -} - -void tx_writec(unsigned char c) -{ - uartc_port_tx_byte(&ser_port, c); -} - -#ifndef IPOD_ACCESSORY_PROTOCOL -void serial_setup(void) -{ - uartc_port_open(&ser_port); - - /* set a default configuration, Tx and Rx modes are - disabled when the port is initialized */ - uartc_port_config(&ser_port, ULCON_DATA_BITS_8, - ULCON_PARITY_NONE, ULCON_STOP_BITS_1); - uartc_port_set_bitrate_raw(&ser_port, BRDATA_115200); - - /* enable Tx interrupt request or POLLING mode */ - uartc_port_set_tx_mode(&ser_port, UCON_MODE_INTREQ); - - logf("[%lu] "MODEL_NAME" port %d ready!", USEC_TIMER, ser_port.id); -} - - -#else /* IPOD_ACCESSORY_PROTOCOL */ -#include "kernel.h" -#include "pmu-target.h" -#include "iap.h" - -static enum { - ABR_STATUS_LAUNCHED, /* ST_SYNC */ - ABR_STATUS_SYNCING, /* ST_SOF */ - ABR_STATUS_DONE -} abr_status; - -static int bitrate = 0; -static bool acc_plugged = false; - -static void serial_acc_tick(void) -{ - bool plugged = pmu_accessory_present(); - if (acc_plugged != plugged) - { - acc_plugged = plugged; - if (acc_plugged) - { - uartc_open(ser_port.uartc); - uartc_port_open(&ser_port); - /* set a default configuration, Tx and Rx modes are - disabled when the port is initialized */ - uartc_port_config(&ser_port, ULCON_DATA_BITS_8, - ULCON_PARITY_NONE, ULCON_STOP_BITS_1); - uartc_port_set_tx_mode(&ser_port, UCON_MODE_INTREQ); - serial_bitrate(bitrate); - } - else - { - uartc_port_close(&ser_port); - uartc_close(ser_port.uartc); - } - } -} - -void serial_setup(void) -{ - uartc_close(ser_port.uartc); - tick_add_task(serial_acc_tick); -} - -void serial_bitrate(int rate) -{ - bitrate = rate; - if (!acc_plugged) - return; - - logf("[%lu] serial_bitrate(%d)", USEC_TIMER, rate); - - if (rate == 0) { - /* Using auto-bitrate (ABR) to detect accessory Tx speed: - * - * + Here: - * - Disable Rx logic to clean the FIFO and the shift - * register, thus no Rx data interrupts are generated. - * - Launch ABR and wait for a low pulse in Rx line. - * - * + In ISR, when a low pulse is detected (ideally it is the - * start bit of 0xff): - * - Calculate and configure detected speed. - * - Enable Rx to verify that the next received data frame - * is 0x55 or 0xff: - * - If so, it's assumed bit rate is correctly detected, - * it will not be modified until speed is changed using - * RB options menu. - * - If not, reset iAP state machine and launch a new ABR. - */ - uartc_port_set_rx_mode(&ser_port, UCON_MODE_DISABLED); - uartc_port_abr_start(&ser_port); - abr_status = ABR_STATUS_LAUNCHED; - } - else { - uint32_t brdata; - if (rate == 57600) brdata = BRDATA_57600; - else if (rate == 38400) brdata = BRDATA_38400; - else if (rate == 19200) brdata = BRDATA_19200; - else brdata = BRDATA_9600; - uartc_port_abr_stop(&ser_port); /* abort ABR if already launched */ - uartc_port_set_bitrate_raw(&ser_port, brdata); - uartc_port_set_rx_mode(&ser_port, UCON_MODE_INTREQ); - abr_status = ABR_STATUS_DONE; - } -} - -static void iap_rx_isr(int len, char *data, char *err, uint32_t abr_cnt) -{ - /* ignore Rx errors, upper layer will discard bad packets */ - (void) err; - - static int sync_retry; - - if (abr_status == ABR_STATUS_LAUNCHED) { - /* autobauding */ - if (abr_cnt) { - #define BR2CNT(s) (IPOD6G_UART_CLK_HZ / (unsigned)(s)) - if (abr_cnt < BR2CNT(57600*1.1) || abr_cnt > BR2CNT(9600*0.9)) { - /* detected speed out of range, relaunch ABR */ - uartc_port_abr_start(&ser_port); - return; - } - /* valid speed detected, select it */ - uint32_t brdata; - if (abr_cnt < BR2CNT(48000)) brdata = BRDATA_57600; - else if (abr_cnt < BR2CNT(33600)) brdata = BRDATA_38400; - else if (abr_cnt < BR2CNT(24000)) brdata = BRDATA_28800; - else if (abr_cnt < BR2CNT(14400)) brdata = BRDATA_19200; - else brdata = BRDATA_9600; - - /* set detected speed */ - uartc_port_set_bitrate_raw(&ser_port, brdata); - uartc_port_set_rx_mode(&ser_port, UCON_MODE_INTREQ); - - /* enter SOF state */ - iap_getc(0xff); - - abr_status = ABR_STATUS_SYNCING; - sync_retry = 2; /* we are expecting [0xff] 0x55 */ - } - } - - /* process received data */ - while (len--) - { - bool sync_done = !iap_getc(*data++); - - if (abr_status == ABR_STATUS_SYNCING) - { - if (sync_done) { - abr_status = ABR_STATUS_DONE; - } - else if (--sync_retry == 0) { - /* invalid speed detected, relaunch ABR - discarding remaining data (if any) */ - serial_bitrate(0); - break; - } - } - } -} -#endif /* IPOD_ACCESSORY_PROTOCOL */ diff --git a/firmware/target/arm/s5l8702/ipod6g/storage_ata-6g.c b/firmware/target/arm/s5l8702/ipod6g/storage_ata-6g.c new file mode 100644 index 0000000000..ef39a5cabb --- /dev/null +++ b/firmware/target/arm/s5l8702/ipod6g/storage_ata-6g.c @@ -0,0 +1,1131 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2007 Dave Chapman + * + * 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 "thread.h" +#include "disk.h" +#include "storage.h" +#include "timer.h" +#include "kernel.h" +#include "string.h" +#include "power.h" +#include "panic.h" +#include "mmu-arm.h" +#include "mmcdefs-target.h" +#include "s5l8702.h" +#include "led.h" +#include "ata_idle_notify.h" +#include "disk_cache.h" + + +#ifndef ATA_RETRIES +#define ATA_RETRIES 3 +#endif + + +#define CEATA_POWERUP_TIMEOUT 20000000 +#define CEATA_COMMAND_TIMEOUT 1000000 +#define CEATA_DAT_NONBUSY_TIMEOUT 5000000 +#define CEATA_MMC_RCA 1 + + +/** static, private data **/ +static uint8_t ceata_taskfile[16] STORAGE_ALIGN_ATTR; +static uint16_t ata_identify_data[0x100] STORAGE_ALIGN_ATTR; +static bool ceata; +static bool ata_lba48; +static bool ata_dma; +static uint64_t ata_total_sectors; +static struct mutex ata_mutex; +static struct semaphore ata_wakeup; +static uint32_t ata_dma_flags; +static long ata_last_activity_value = -1; +static long ata_sleep_timeout = 20 * HZ; +static uint32_t ata_stack[(DEFAULT_STACK_SIZE + 0x400) / 4]; +static bool ata_powered; +static const int ata_retries = ATA_RETRIES; +static const bool ata_error_srst = true; +static struct semaphore mmc_wakeup; +static struct semaphore mmc_comp_wakeup; +static int spinup_time = 0; +static int dma_mode = 0; +static char aligned_buffer[SECTOR_SIZE] STORAGE_ALIGN_ATTR; + +static int ata_reset(void); + + +static uint16_t ata_read_cbr(uint32_t volatile* reg) +{ + while (!(ATA_PIO_READY & 2)); + volatile uint32_t dummy __attribute__((unused)) = *reg; + while (!(ATA_PIO_READY & 1)); + return ATA_PIO_RDATA; +} + +static void ata_write_cbr(uint32_t volatile* reg, uint16_t data) +{ + while (!(ATA_PIO_READY & 2)); + *reg = data; +} + +static int ata_wait_for_not_bsy(long timeout) +{ + long startusec = USEC_TIMER; + while (true) + { + uint8_t csd = ata_read_cbr(&ATA_PIO_CSD); + if (!(csd & BIT(7))) return 0; + if (TIMEOUT_EXPIRED(startusec, timeout)) RET_ERR(0); + yield(); + } +} + +static int ata_wait_for_rdy(long timeout) +{ + long startusec = USEC_TIMER; + PASS_RC(ata_wait_for_not_bsy(timeout), 1, 0); + while (true) + { + uint8_t dad = ata_read_cbr(&ATA_PIO_DAD); + if (dad & BIT(6)) return 0; + if (TIMEOUT_EXPIRED(startusec, timeout)) RET_ERR(1); + yield(); + } +} + +static int ata_wait_for_start_of_transfer(long timeout) +{ + long startusec = USEC_TIMER; + PASS_RC(ata_wait_for_not_bsy(timeout), 2, 0); + while (true) + { + uint8_t dad = ata_read_cbr(&ATA_PIO_DAD); + if (dad & BIT(0)) RET_ERR(1); + if ((dad & (BIT(7) | BIT(3))) == BIT(3)) return 0; + if (TIMEOUT_EXPIRED(startusec, timeout)) RET_ERR(2); + yield(); + } +} + +static int ata_wait_for_end_of_transfer(long timeout) +{ + PASS_RC(ata_wait_for_not_bsy(timeout), 2, 0); + uint8_t dad = ata_read_cbr(&ATA_PIO_DAD); + if (dad & BIT(0)) RET_ERR(1); + if ((dad & (BIT(3) | BITRANGE(5, 7))) == BIT(6)) return 0; + RET_ERR(2); +} + +static int mmc_dsta_check_command_success(bool disable_crc) +{ + int rc = 0; + uint32_t dsta = SDCI_DSTA; + if (dsta & SDCI_DSTA_RESTOUTE) rc |= 1; + if (dsta & SDCI_DSTA_RESENDE) rc |= 2; + if (dsta & SDCI_DSTA_RESINDE) rc |= 4; + if (!disable_crc) + if (dsta & SDCI_DSTA_RESCRCE) + rc |= 8; + if (rc) RET_ERR(rc); + return 0; +} + +static bool mmc_send_command(uint32_t cmd, uint32_t arg, uint32_t* result, int timeout) +{ + long starttime = USEC_TIMER; + while ((SDCI_STATE & SDCI_STATE_CMD_STATE_MASK) != SDCI_STATE_CMD_STATE_CMD_IDLE) + { + if (TIMEOUT_EXPIRED(starttime, timeout)) RET_ERR(0); + yield(); + } + SDCI_STAC = SDCI_STAC_CLR_CMDEND | SDCI_STAC_CLR_BIT_3 + | SDCI_STAC_CLR_RESEND | SDCI_STAC_CLR_DATEND + | SDCI_STAC_CLR_DAT_CRCEND | SDCI_STAC_CLR_CRC_STAEND + | SDCI_STAC_CLR_RESTOUTE | SDCI_STAC_CLR_RESENDE + | SDCI_STAC_CLR_RESINDE | SDCI_STAC_CLR_RESCRCE + | SDCI_STAC_CLR_WR_DATCRCE | SDCI_STAC_CLR_RD_DATCRCE + | SDCI_STAC_CLR_RD_DATENDE0 | SDCI_STAC_CLR_RD_DATENDE1 + | SDCI_STAC_CLR_RD_DATENDE2 | SDCI_STAC_CLR_RD_DATENDE3 + | SDCI_STAC_CLR_RD_DATENDE4 | SDCI_STAC_CLR_RD_DATENDE5 + | SDCI_STAC_CLR_RD_DATENDE6 | SDCI_STAC_CLR_RD_DATENDE7; + SDCI_ARGU = arg; + SDCI_CMD = cmd; + if (!(SDCI_DSTA & SDCI_DSTA_CMDRDY)) RET_ERR(1); + SDCI_CMD = cmd | SDCI_CMD_CMDSTR; + long sleepbase = USEC_TIMER; + while (TIMEOUT_EXPIRED(sleepbase, 1000)) yield(); + while (!(SDCI_DSTA & SDCI_DSTA_CMDEND)) + { + if (TIMEOUT_EXPIRED(starttime, timeout)) RET_ERR(2); + yield(); + } + if ((cmd & SDCI_CMD_RES_TYPE_MASK) != SDCI_CMD_RES_TYPE_NONE) + { + while (!(SDCI_DSTA & SDCI_DSTA_RESEND)) + { + if (TIMEOUT_EXPIRED(starttime, timeout)) RET_ERR(3); + yield(); + } + if (cmd & SDCI_CMD_RES_BUSY) + while (SDCI_DSTA & SDCI_DSTA_DAT_BUSY) + { + if (TIMEOUT_EXPIRED(starttime, CEATA_DAT_NONBUSY_TIMEOUT)) RET_ERR(4); + yield(); + } + } + bool nocrc = (cmd & SDCI_CMD_RES_SIZE_MASK) == SDCI_CMD_RES_SIZE_136; + PASS_RC(mmc_dsta_check_command_success(nocrc), 3, 5); + if (result) *result = SDCI_RESP0; + return 0; +} + +static int mmc_get_card_status(uint32_t* result) +{ + return mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_SEND_STATUS) + | SDCI_CMD_CMD_TYPE_AC | SDCI_CMD_RES_TYPE_R1 + | SDCI_CMD_RES_SIZE_48 | SDCI_CMD_NCR_NID_NCR, + MMC_CMD_SEND_STATUS_RCA(CEATA_MMC_RCA), result, CEATA_COMMAND_TIMEOUT); +} + +static int mmc_init(void) +{ + sleep(HZ / 10); + PASS_RC(mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_GO_IDLE_STATE) + | SDCI_CMD_CMD_TYPE_BC | SDCI_CMD_RES_TYPE_NONE + | SDCI_CMD_RES_SIZE_48 | SDCI_CMD_NCR_NID_NID, + 0, NULL, CEATA_COMMAND_TIMEOUT), 3, 0); + long startusec = USEC_TIMER; + uint32_t result; + do + { + if (TIMEOUT_EXPIRED(startusec, CEATA_POWERUP_TIMEOUT)) RET_ERR(1); + sleep(HZ / 100); + PASS_RC(mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_SEND_OP_COND) + | SDCI_CMD_CMD_TYPE_BCR | SDCI_CMD_RES_TYPE_R3 + | SDCI_CMD_RES_SIZE_48 | SDCI_CMD_NCR_NID_NID, + MMC_CMD_SEND_OP_COND_OCR(MMC_OCR_270_360), + NULL, CEATA_COMMAND_TIMEOUT), 3, 2); + result = SDCI_RESP0; + } + while (!(result & MMC_OCR_POWER_UP_DONE)); + PASS_RC(mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_ALL_SEND_CID) + | SDCI_CMD_CMD_TYPE_BCR | SDCI_CMD_RES_TYPE_R2 + | SDCI_CMD_RES_SIZE_136 | SDCI_CMD_NCR_NID_NID, + 0, NULL, CEATA_COMMAND_TIMEOUT), 3, 3); + PASS_RC(mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_SET_RELATIVE_ADDR) + | SDCI_CMD_CMD_TYPE_BCR | SDCI_CMD_RES_TYPE_R1 + | SDCI_CMD_RES_SIZE_48 | SDCI_CMD_NCR_NID_NCR, + MMC_CMD_SET_RELATIVE_ADDR_RCA(CEATA_MMC_RCA), + NULL, CEATA_COMMAND_TIMEOUT), 3, 4); + PASS_RC(mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_SELECT_CARD) + | SDCI_CMD_CMD_TYPE_AC | SDCI_CMD_RES_TYPE_R1 + | SDCI_CMD_RES_SIZE_48 | SDCI_CMD_NCR_NID_NCR, + MMC_CMD_SELECT_CARD_RCA(CEATA_MMC_RCA), + NULL, CEATA_COMMAND_TIMEOUT), 3, 5); + PASS_RC(mmc_get_card_status(&result), 3, 6); + if ((result & MMC_STATUS_CURRENT_STATE_MASK) != MMC_STATUS_CURRENT_STATE_TRAN) RET_ERR(7); + return 0; +} + +static int mmc_fastio_write(uint32_t addr, uint32_t data) +{ + return mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_FAST_IO) + | SDCI_CMD_CMD_TYPE_AC | SDCI_CMD_RES_TYPE_R4 + | SDCI_CMD_RES_SIZE_48 | SDCI_CMD_NCR_NID_NCR, + MMC_CMD_FAST_IO_RCA(CEATA_MMC_RCA) | MMC_CMD_FAST_IO_DIRECTION_WRITE + | MMC_CMD_FAST_IO_ADDRESS(addr) | MMC_CMD_FAST_IO_DATA(data), + NULL, CEATA_COMMAND_TIMEOUT); +} + +static int mmc_fastio_read(uint32_t addr, uint32_t* data) +{ + return mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_FAST_IO) + | SDCI_CMD_CMD_TYPE_AC | SDCI_CMD_RES_TYPE_R4 + | SDCI_CMD_RES_SIZE_48 | SDCI_CMD_NCR_NID_NCR, + MMC_CMD_FAST_IO_RCA(CEATA_MMC_RCA) | MMC_CMD_FAST_IO_DIRECTION_READ + | MMC_CMD_FAST_IO_ADDRESS(addr), data, CEATA_COMMAND_TIMEOUT); +} + +static int ceata_soft_reset(void) +{ + PASS_RC(mmc_fastio_write(6, 4), 2, 0); + sleep(HZ / 100); + PASS_RC(mmc_fastio_write(6, 0), 2, 1); + sleep(HZ / 100); + long startusec = USEC_TIMER; + uint32_t status; + do + { + PASS_RC(mmc_fastio_read(0xf, &status), 2, 2); + if (TIMEOUT_EXPIRED(startusec, CEATA_POWERUP_TIMEOUT)) RET_ERR(3); + sleep(HZ / 100); + } + while (status & 0x80); + return 0; +} + +static int mmc_dsta_check_data_success(void) +{ + int rc = 0; + uint32_t dsta = SDCI_DSTA; + if (dsta & (SDCI_DSTA_WR_DATCRCE | SDCI_DSTA_RD_DATCRCE)) + { + if (dsta & SDCI_DSTA_WR_DATCRCE) rc |= 1; + if (dsta & SDCI_DSTA_RD_DATCRCE) rc |= 2; + if ((dsta & SDCI_DSTA_WR_CRC_STATUS_MASK) == SDCI_DSTA_WR_CRC_STATUS_TXERR) rc |= 4; + else if ((dsta & SDCI_DSTA_WR_CRC_STATUS_MASK) == SDCI_DSTA_WR_CRC_STATUS_CARDERR) rc |= 8; + } + if (dsta & (SDCI_DSTA_RD_DATENDE0 | SDCI_DSTA_RD_DATENDE1 | SDCI_DSTA_RD_DATENDE2 + | SDCI_DSTA_RD_DATENDE3 | SDCI_DSTA_RD_DATENDE4 | SDCI_DSTA_RD_DATENDE5 + | SDCI_DSTA_RD_DATENDE6 | SDCI_DSTA_RD_DATENDE7)) + rc |= 16; + if (rc) RET_ERR(rc); + return 0; +} + +static void mmc_discard_irq(void) +{ + SDCI_IRQ = SDCI_IRQ_DAT_DONE_INT | SDCI_IRQ_MASK_MASK_IOCARD_IRQ_INT + | SDCI_IRQ_MASK_MASK_READ_WAIT_INT; + semaphore_wait(&mmc_wakeup, 0); +} + +static int ceata_read_multiple_register(uint32_t addr, void* dest, uint32_t size) +{ + if (size > 0x10) RET_ERR(0); + mmc_discard_irq(); + SDCI_DMASIZE = size; + SDCI_DMACOUNT = 1; + SDCI_DMAADDR = dest; + SDCI_DCTRL = SDCI_DCTRL_TXFIFORST | SDCI_DCTRL_RXFIFORST; + commit_discard_dcache(); + PASS_RC(mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_CEATA_RW_MULTIPLE_REG) + | SDCI_CMD_CMD_TYPE_ADTC | SDCI_CMD_RES_TYPE_R1 + | SDCI_CMD_RES_SIZE_48 | SDCI_CMD_NCR_NID_NCR, + MMC_CMD_CEATA_RW_MULTIPLE_REG_DIRECTION_READ + | MMC_CMD_CEATA_RW_MULTIPLE_REG_ADDRESS(addr & 0xfc) + | MMC_CMD_CEATA_RW_MULTIPLE_REG_COUNT(size & 0xfc), + NULL, CEATA_COMMAND_TIMEOUT), 2, 1); + if (semaphore_wait(&mmc_wakeup, CEATA_COMMAND_TIMEOUT * HZ / 1000000) + == OBJ_WAIT_TIMEDOUT) RET_ERR(2); + PASS_RC(mmc_dsta_check_data_success(), 2, 3); + return 0; +} + +static int ceata_write_multiple_register(uint32_t addr, void* dest, uint32_t size) +{ + uint32_t i; + if (size > 0x10) RET_ERR(0); + mmc_discard_irq(); + SDCI_DMASIZE = size; + SDCI_DMACOUNT = 0; + SDCI_DCTRL = SDCI_DCTRL_TXFIFORST | SDCI_DCTRL_RXFIFORST; + PASS_RC(mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_CEATA_RW_MULTIPLE_REG) + | SDCI_CMD_CMD_TYPE_ADTC | SDCI_CMD_CMD_RD_WR + | SDCI_CMD_RES_BUSY | SDCI_CMD_RES_TYPE_R1 + | SDCI_CMD_RES_SIZE_48 | SDCI_CMD_NCR_NID_NCR, + MMC_CMD_CEATA_RW_MULTIPLE_REG_DIRECTION_WRITE + | MMC_CMD_CEATA_RW_MULTIPLE_REG_ADDRESS(addr & 0xfc) + | MMC_CMD_CEATA_RW_MULTIPLE_REG_COUNT(size & 0xfc), + NULL, CEATA_COMMAND_TIMEOUT), 3, 1); + SDCI_DCTRL = SDCI_DCTRL_TRCONT_TX; + for (i = 0; i < size / 4; i++) SDCI_DATA = ((uint32_t*)dest)[i]; + long startusec = USEC_TIMER; + if (semaphore_wait(&mmc_wakeup, CEATA_COMMAND_TIMEOUT * HZ / 1000000) + == OBJ_WAIT_TIMEDOUT) RET_ERR(2); + while ((SDCI_STATE & SDCI_STATE_DAT_STATE_MASK) != SDCI_STATE_DAT_STATE_IDLE) + { + if (TIMEOUT_EXPIRED(startusec, CEATA_COMMAND_TIMEOUT)) RET_ERR(3); + yield(); + } + PASS_RC(mmc_dsta_check_data_success(), 3, 4); + return 0; +} + +static int ceata_init(int buswidth) +{ + uint32_t result; + PASS_RC(mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_SWITCH) | SDCI_CMD_RES_BUSY + | SDCI_CMD_CMD_TYPE_AC | SDCI_CMD_RES_TYPE_R1 + | SDCI_CMD_RES_SIZE_48 | SDCI_CMD_NCR_NID_NCR, + MMC_CMD_SWITCH_ACCESS_WRITE_BYTE + | MMC_CMD_SWITCH_INDEX(MMC_CMD_SWITCH_FIELD_HS_TIMING) + | MMC_CMD_SWITCH_VALUE(MMC_CMD_SWITCH_FIELD_HS_TIMING_HIGH_SPEED), + &result, CEATA_COMMAND_TIMEOUT), 3, 0); + if (result & MMC_STATUS_SWITCH_ERROR) RET_ERR(1); + if (buswidth > 1) + { + int setting; + if (buswidth == 4) setting = MMC_CMD_SWITCH_FIELD_BUS_WIDTH_4BIT; + else if (buswidth == 8) setting = MMC_CMD_SWITCH_FIELD_BUS_WIDTH_8BIT; + else setting = MMC_CMD_SWITCH_FIELD_BUS_WIDTH_1BIT; + PASS_RC(mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_SWITCH) | SDCI_CMD_RES_BUSY + | SDCI_CMD_CMD_TYPE_AC | SDCI_CMD_RES_TYPE_R1 + | SDCI_CMD_RES_SIZE_48 | SDCI_CMD_NCR_NID_NCR, + MMC_CMD_SWITCH_ACCESS_WRITE_BYTE + | MMC_CMD_SWITCH_INDEX(MMC_CMD_SWITCH_FIELD_BUS_WIDTH) + | MMC_CMD_SWITCH_VALUE(setting), + &result, CEATA_COMMAND_TIMEOUT), 3, 2); + if (result & MMC_STATUS_SWITCH_ERROR) RET_ERR(3); + if (buswidth == 4) + SDCI_CTRL = (SDCI_CTRL & ~SDCI_CTRL_BUS_WIDTH_MASK) | SDCI_CTRL_BUS_WIDTH_4BIT; + else if (buswidth == 8) + SDCI_CTRL = (SDCI_CTRL & ~SDCI_CTRL_BUS_WIDTH_MASK) | SDCI_CTRL_BUS_WIDTH_8BIT; + } + PASS_RC(ceata_soft_reset(), 3, 4); + PASS_RC(ceata_read_multiple_register(0, ceata_taskfile, 0x10), 3, 5); + if (ceata_taskfile[0xc] != 0xce || ceata_taskfile[0xd] != 0xaa) RET_ERR(6); + PASS_RC(mmc_fastio_write(6, 0), 3, 7); + return 0; +} + +static int ceata_check_error(void) +{ + uint32_t status, error; + PASS_RC(mmc_fastio_read(0xf, &status), 2, 0); + if (status & 1) + { + PASS_RC(mmc_fastio_read(0x9, &error), 2, 1); + RET_ERR((error << 2) | 2); + } + return 0; +} + +static int ceata_wait_idle(void) +{ + long startusec = USEC_TIMER; + while (true) + { + uint32_t status; + PASS_RC(mmc_fastio_read(0xf, &status), 1, 0); + if (!(status & 0x88)) return 0; + if (TIMEOUT_EXPIRED(startusec, CEATA_DAT_NONBUSY_TIMEOUT)) RET_ERR(1); + sleep(HZ / 20); + } +} + +static int ceata_cancel_command(void) +{ + *((uint32_t volatile*)0x3cf00200) = 0x9000e; + udelay(1); + *((uint32_t volatile*)0x3cf00200) = 0x9000f; + udelay(1); + *((uint32_t volatile*)0x3cf00200) = 0x90003; + udelay(1); + PASS_RC(mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_STOP_TRANSMISSION) + | SDCI_CMD_CMD_TYPE_AC | SDCI_CMD_RES_TYPE_R1 | SDCI_CMD_RES_BUSY + | SDCI_CMD_RES_SIZE_48 | SDCI_CMD_NCR_NID_NCR, + 0, NULL, CEATA_COMMAND_TIMEOUT), 1, 0); + PASS_RC(ceata_wait_idle(), 1, 1); + return 0; +} + +static int ceata_rw_multiple_block(bool write, void* buf, uint32_t count, long timeout) +{ + mmc_discard_irq(); + uint32_t responsetype; + uint32_t cmdtype; + uint32_t direction; + if (write) + { + cmdtype = SDCI_CMD_CMD_TYPE_ADTC | SDCI_CMD_CMD_RD_WR; + responsetype = SDCI_CMD_RES_TYPE_R1 | SDCI_CMD_RES_BUSY; + direction = MMC_CMD_CEATA_RW_MULTIPLE_BLOCK_DIRECTION_WRITE; + } + else + { + cmdtype = SDCI_CMD_CMD_TYPE_ADTC; + responsetype = SDCI_CMD_RES_TYPE_R1; + direction = MMC_CMD_CEATA_RW_MULTIPLE_BLOCK_DIRECTION_READ; + } + SDCI_DMASIZE = 0x200; + SDCI_DMAADDR = buf; + SDCI_DMACOUNT = count; + SDCI_DCTRL = SDCI_DCTRL_TXFIFORST | SDCI_DCTRL_RXFIFORST; + commit_discard_dcache(); + PASS_RC(mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_CEATA_RW_MULTIPLE_BLOCK) + | SDCI_CMD_CMD_TYPE_ADTC | cmdtype | responsetype + | SDCI_CMD_RES_SIZE_48 | SDCI_CMD_NCR_NID_NCR, + direction | MMC_CMD_CEATA_RW_MULTIPLE_BLOCK_COUNT(count), + NULL, CEATA_COMMAND_TIMEOUT), 3, 0); + if (write) SDCI_DCTRL = SDCI_DCTRL_TRCONT_TX; + if (semaphore_wait(&mmc_wakeup, timeout) == OBJ_WAIT_TIMEDOUT) + { + PASS_RC(ceata_cancel_command(), 3, 1); + RET_ERR(2); + } + PASS_RC(mmc_dsta_check_data_success(), 3, 3); + if (semaphore_wait(&mmc_comp_wakeup, timeout) == OBJ_WAIT_TIMEDOUT) + { + PASS_RC(ceata_cancel_command(), 3, 4); + RET_ERR(5); + } + PASS_RC(ceata_check_error(), 3, 6); + return 0; +} + +static int ata_identify(uint16_t* buf) +{ + int i; + if (ceata) + { + memset(ceata_taskfile, 0, 16); + ceata_taskfile[0xf] = 0xec; + PASS_RC(ceata_wait_idle(), 2, 0); + PASS_RC(ceata_write_multiple_register(0, ceata_taskfile, 16), 2, 1); + PASS_RC(ceata_rw_multiple_block(false, buf, 1, CEATA_COMMAND_TIMEOUT * HZ / 1000000), 2, 2); + } + else + { + PASS_RC(ata_wait_for_not_bsy(10000000), 1, 0); + ata_write_cbr(&ATA_PIO_DVR, 0); + ata_write_cbr(&ATA_PIO_CSD, 0xec); + PASS_RC(ata_wait_for_start_of_transfer(10000000), 1, 1); + for (i = 0; i < 0x100; i++) buf[i] = ata_read_cbr(&ATA_PIO_DTR); + } + return 0; +} + +static void ata_set_active(void) +{ + ata_last_activity_value = current_tick; +} + +bool ata_disk_is_active(void) +{ + return ata_powered; +} + +static int ata_set_feature(uint32_t feature, uint32_t param) +{ + if (ceata) + { + memset(ceata_taskfile, 0, 16); + ceata_taskfile[0x1] = feature; + ceata_taskfile[0x2] = param; + ceata_taskfile[0xf] = 0xef; + PASS_RC(ceata_wait_idle(), 2, 0); + PASS_RC(ceata_write_multiple_register(0, ceata_taskfile, 16), 2, 1); + PASS_RC(ceata_wait_idle(), 2, 2); + } + else + { + PASS_RC(ata_wait_for_rdy(2000000), 2, 0); + ata_write_cbr(&ATA_PIO_DVR, 0); + ata_write_cbr(&ATA_PIO_FED, feature); + ata_write_cbr(&ATA_PIO_SCR, param); + ata_write_cbr(&ATA_PIO_CSD, 0xef); + PASS_RC(ata_wait_for_rdy(2000000), 2, 1); + } + return 0; +} + +/* + * ATA_UDMA_TIME register is documented on s3c6400 datasheet, information + * included in s5l8700 datasheet is wrong or not valid for s5l8702. + * + * On Classic (boosted): + * HClk = 108 MHz. -> T = ~9.26 ns. + * + * Configured values (in nanoseconds): + * + * UDMA ATA_UDMA_TIME tACK tRP tSS tDVS tDVH Tcyc WR(MB/s) + * 0 0x4071152 27.8 166.7 55.6 74.1 46.3 120.4 16.6 + * 1 0x2050d52 27.8 129.6 55.6 55.6 27.8 83.4 24 + * 2 0x2030a52 27.8 101.8 55.6 37 27.8 64.8 30.9 + * 3 0x1020a52 27.8 101.8 55.6 27.8 18.5 46.3 43.2 + * 4 0x1010a52 27.8 101.8 55.6 18.5 18.5 37 54 + * + * Tcyc = tDVS+tDVH + * WR[bytes/s] = 1/Tcyc[s] * 2[bytes] + */ +static int ata_power_up(void) +{ + ata_set_active(); + ide_power_enable(true); + long spinup_start = current_tick; + if (ceata) + { + ata_lba48 = true; + ata_dma = true; + PCON(8) = 0x33333333; + PCON(9) = 0x00000033; + PCON(11) |= 0xf; + *((uint32_t volatile*)0x38a00000) = 0; + *((uint32_t volatile*)0x38700000) = 0; + PWRCON(0) &= ~(1 << 9); + SDCI_RESET = 0xa5; + sleep(HZ / 100); + *((uint32_t volatile*)0x3cf00380) = 0; + *((uint32_t volatile*)0x3cf0010c) = 0xff; + SDCI_CTRL = SDCI_CTRL_SDCIEN | SDCI_CTRL_CLK_SEL_SDCLK + | SDCI_CTRL_BIT_8 | SDCI_CTRL_BIT_14; + SDCI_CDIV = SDCI_CDIV_CLKDIV(260); + *((uint32_t volatile*)0x3cf00200) = 0xb000f; + SDCI_IRQ_MASK = SDCI_IRQ_MASK_MASK_DAT_DONE_INT | SDCI_IRQ_MASK_MASK_IOCARD_IRQ_INT; + PASS_RC(mmc_init(), 3, 0); + SDCI_CDIV = SDCI_CDIV_CLKDIV(4); + sleep(HZ / 100); + PASS_RC(ceata_init(8), 3, 1); + PASS_RC(ata_identify(ata_identify_data), 3, 2); + dma_mode = 0x44; + } + else + { + PCON(7) = 0x44444444; + PCON(8) = 0x44444444; + PCON(9) = 0x44444444; + PCON(10) = (PCON(10) & ~0xffff) | 0x4444; + PWRCON(0) &= ~(1 << 5); + ATA_CFG = BIT(0); + sleep(HZ / 100); + ATA_CFG = 0; + sleep(HZ / 100); + ATA_SWRST = BIT(0); + sleep(HZ / 100); + ATA_SWRST = 0; + sleep(HZ / 10); + ATA_CONTROL = BIT(0); + sleep(HZ / 5); + ATA_PIO_TIME = 0x191f7; + ATA_PIO_LHR = 0; + ATA_CFG = BIT(6); + while (!(ATA_PIO_READY & BIT(1))) yield(); + PASS_RC(ata_identify(ata_identify_data), 3, 3); + uint32_t piotime = 0x11f3; + uint32_t mdmatime = 0x1c175; + uint32_t udmatime = 0x4071152; + uint32_t param = 0; + ata_dma_flags = 0; + ata_lba48 = ata_identify_data[83] & BIT(10) ? true : false; + if (ata_identify_data[53] & BIT(1)) + { + if (ata_identify_data[64] & BIT(1)) piotime = 0x2072; + else if (ata_identify_data[64] & BIT(0)) piotime = 0x7083; + } + if (ata_identify_data[63] & BIT(2)) + { + mdmatime = 0x5072; + param = 0x22; + } + else if (ata_identify_data[63] & BIT(1)) + { + mdmatime = 0x7083; + param = 0x21; + } + if (ata_identify_data[63] & BITRANGE(0, 2)) + { + ata_dma_flags = BIT(3) | BIT(10); + param |= 0x20; + } + if (ata_identify_data[53] & BIT(2)) + { + if (ata_identify_data[88] & BIT(4)) + { + udmatime = 0x1010a52; + param = 0x44; + } + else if (ata_identify_data[88] & BIT(3)) + { + udmatime = 0x1020a52; + param = 0x43; + } + else if (ata_identify_data[88] & BIT(2)) + { + udmatime = 0x2030a52; + param = 0x42; + } + else if (ata_identify_data[88] & BIT(1)) + { + udmatime = 0x2050d52; + param = 0x41; + } + else if (ata_identify_data[88] & BIT(0)) + { + param = 0x40; + } + if (ata_identify_data[88] & BITRANGE(0, 4)) + { + ata_dma_flags = BIT(2) | BIT(3) | BIT(9) | BIT(10); + } + } + ata_dma = param ? true : false; + dma_mode = param; + PASS_RC(ata_set_feature(0x03, param), 3, 4); + if (ata_identify_data[82] & BIT(5)) + PASS_RC(ata_set_feature(0x02, 0), 3, 5); + if (ata_identify_data[82] & BIT(6)) PASS_RC(ata_set_feature(0xaa, 0), 3, 6); + ATA_PIO_TIME = piotime; + ATA_MDMA_TIME = mdmatime; + ATA_UDMA_TIME = udmatime; + } + spinup_time = current_tick - spinup_start; + if (ata_lba48) + ata_total_sectors = ata_identify_data[100] + | (((uint64_t)ata_identify_data[101]) << 16) + | (((uint64_t)ata_identify_data[102]) << 32) + | (((uint64_t)ata_identify_data[103]) << 48); + else ata_total_sectors = ata_identify_data[60] | (((uint32_t)ata_identify_data[61]) << 16); + ata_total_sectors >>= 3; + ata_powered = true; + ata_set_active(); + return 0; +} + +static void ata_power_down(void) +{ + if (!ata_powered) return; + if (ceata) + { + memset(ceata_taskfile, 0, 16); + ceata_taskfile[0xf] = 0xe0; + ceata_wait_idle(); + ceata_write_multiple_register(0, ceata_taskfile, 16); + ceata_wait_idle(); + sleep(HZ); + PWRCON(0) |= (1 << 9); + } + else + { + ata_wait_for_rdy(1000000); + ata_write_cbr(&ATA_PIO_DVR, 0); + ata_write_cbr(&ATA_PIO_CSD, 0xe0); + ata_wait_for_rdy(1000000); + sleep(HZ / 30); + ATA_CONTROL = 0; + while (!(ATA_CONTROL & BIT(1))) yield(); + PWRCON(0) |= (1 << 5); + } + PCON(7) = 0; + PCON(8) = 0; + PCON(9) = 0; + PCON(10) &= ~0xffff; + PCON(11) &= ~0xf; + ide_power_enable(false); + ata_powered = false; +} + +static int ata_rw_chunk_internal(uint64_t sector, uint32_t cnt, void* buffer, bool write) +{ + if (ceata) + { + memset(ceata_taskfile, 0, 16); + ceata_taskfile[0x2] = cnt >> 5; + ceata_taskfile[0x3] = sector >> 21; + ceata_taskfile[0x4] = sector >> 29; + ceata_taskfile[0x5] = sector >> 37; + ceata_taskfile[0xa] = cnt << 3; + ceata_taskfile[0xb] = sector << 3; + ceata_taskfile[0xc] = sector >> 5; + ceata_taskfile[0xd] = sector >> 13; + ceata_taskfile[0xf] = write ? 0x35 : 0x25; + PASS_RC(ceata_wait_idle(), 2, 0); + PASS_RC(ceata_write_multiple_register(0, ceata_taskfile, 16), 2, 1); + PASS_RC(ceata_rw_multiple_block(write, buffer, cnt << 3, CEATA_COMMAND_TIMEOUT * HZ / 1000000), 2, 2); + } + else + { + PASS_RC(ata_wait_for_rdy(100000), 2, 0); + ata_write_cbr(&ATA_PIO_DVR, 0); + if (ata_lba48) + { + ata_write_cbr(&ATA_PIO_SCR, cnt >> 5); + ata_write_cbr(&ATA_PIO_SCR, (cnt << 3) & 0xff); + ata_write_cbr(&ATA_PIO_LHR, (sector >> 37) & 0xff); + ata_write_cbr(&ATA_PIO_LMR, (sector >> 29) & 0xff); + ata_write_cbr(&ATA_PIO_LLR, (sector >> 21) & 0xff); + ata_write_cbr(&ATA_PIO_LHR, (sector >> 13) & 0xff); + ata_write_cbr(&ATA_PIO_LMR, (sector >> 5) & 0xff); + ata_write_cbr(&ATA_PIO_LLR, (sector << 3) & 0xff); + ata_write_cbr(&ATA_PIO_DVR, BIT(6)); + if (write) ata_write_cbr(&ATA_PIO_CSD, ata_dma ? 0x35 : 0x39); + else ata_write_cbr(&ATA_PIO_CSD, ata_dma ? 0x25 : 0x29); + } + else + { + ata_write_cbr(&ATA_PIO_SCR, (cnt << 3) & 0xff); + ata_write_cbr(&ATA_PIO_LHR, (sector >> 13) & 0xff); + ata_write_cbr(&ATA_PIO_LMR, (sector >> 5) & 0xff); + ata_write_cbr(&ATA_PIO_LLR, (sector << 3) & 0xff); + ata_write_cbr(&ATA_PIO_DVR, BIT(6) | ((sector >> 21) & 0xf)); + if (write) ata_write_cbr(&ATA_PIO_CSD, ata_dma ? 0xca : 0x30); + else ata_write_cbr(&ATA_PIO_CSD, ata_dma ? 0xc8 : 0xc4); + } + if (ata_dma) + { + PASS_RC(ata_wait_for_start_of_transfer(500000), 2, 1); + if (write) + { + ATA_SBUF_START = buffer; + ATA_SBUF_SIZE = SECTOR_SIZE * cnt; + ATA_CFG |= BIT(4); + } + else + { + ATA_TBUF_START = buffer; + ATA_TBUF_SIZE = SECTOR_SIZE * cnt; + ATA_CFG &= ~BIT(4); + } + ATA_XFR_NUM = SECTOR_SIZE * cnt - 1; + ATA_CFG |= ata_dma_flags; + ATA_CFG &= ~(BIT(7) | BIT(8)); + semaphore_wait(&ata_wakeup, 0); + ATA_IRQ = BITRANGE(0, 4); + ATA_IRQ_MASK = BIT(0); + ATA_COMMAND = BIT(0); + if (semaphore_wait(&ata_wakeup, 500000 * HZ / 1000000) + == OBJ_WAIT_TIMEDOUT) + { + ATA_COMMAND = BIT(1); + ATA_CFG &= ~(BITRANGE(2, 3) | BIT(12)); + RET_ERR(2); + } + ATA_COMMAND = BIT(1); + ATA_CFG &= ~(BITRANGE(2, 3) | BIT(12)); + } + else + { + cnt *= SECTOR_SIZE / 512; + while (cnt--) + { + int i; + PASS_RC(ata_wait_for_start_of_transfer(500000), 2, 1); + if (write) + for (i = 0; i < 256; i++) + ata_write_cbr(&ATA_PIO_DTR, ((uint16_t*)buffer)[i]); + else + for (i = 0; i < 256; i++) + ((uint16_t*)buffer)[i] = ata_read_cbr(&ATA_PIO_DTR); + buffer += 512; + } + } + PASS_RC(ata_wait_for_end_of_transfer(100000), 2, 3); + } + return 0; +} + +static int ata_rw_chunk(uint64_t sector, uint32_t cnt, void* buffer, bool write) +{ + led(true); + int rc = ata_rw_chunk_internal(sector, cnt, buffer, write); + led(false); + return rc; +} + +static int ata_rw_sectors(uint64_t sector, uint32_t count, void* buffer, bool write) +{ + if (STORAGE_OVERLAP((uint32_t)buffer)) + { + while (count) + { + if (write) + memcpy(aligned_buffer, buffer, SECTOR_SIZE); + + PASS_RC(ata_rw_sectors(sector, 1, aligned_buffer, write), 0, 0); + + if (!write) + memcpy(buffer, aligned_buffer, SECTOR_SIZE); + + buffer += SECTOR_SIZE; + sector++; + count--; + } + + return 0; + } + + if (!ata_powered) ata_power_up(); + if (sector + count > ata_total_sectors) RET_ERR(0); + ata_set_active(); + if (ata_dma && write) commit_dcache(); + else if (ata_dma) commit_discard_dcache(); + if (!ceata) ATA_COMMAND = BIT(1); + while (count) + { + uint32_t cnt = MIN(ata_lba48 ? 8192 : 32, count); + int rc = -1; + rc = ata_rw_chunk(sector, cnt, buffer, write); + if (rc && ata_error_srst) ata_reset(); + if (rc && ata_retries) + { + void* buf = buffer; + uint64_t sect; + for (sect = sector; sect < sector + cnt; sect++) + { + rc = -1; + int tries = ata_retries; + while (tries-- && rc) + { + rc = ata_rw_chunk(sect, 1, buf, write); + if (rc && ata_error_srst) ata_reset(); + } + if (rc) break; + buf += SECTOR_SIZE; + } + } + PASS_RC(rc, 1, 1); + buffer += SECTOR_SIZE * cnt; + sector += cnt; + count -= cnt; + } + ata_set_active(); + return 0; +} + +static void ata_thread(void) +{ + while (true) + { + mutex_lock(&ata_mutex); + if (TIME_AFTER(current_tick, ata_last_activity_value + ata_sleep_timeout) && ata_powered) + { + call_storage_idle_notifys(false); + ata_power_down(); + } + mutex_unlock(&ata_mutex); + sleep(HZ / 2); + } +} + +/* API Functions */ +int ata_soft_reset(void) +{ + int rc; + mutex_lock(&ata_mutex); + if (!ata_powered) PASS_RC(ata_power_up(), 1, 0); + ata_set_active(); + if (ceata) rc = ceata_soft_reset(); + else + { + ata_write_cbr(&ATA_PIO_DAD, BIT(1) | BIT(2)); + udelay(10); + ata_write_cbr(&ATA_PIO_DAD, 0); + rc = ata_wait_for_rdy(3000000); + } + ata_set_active(); + mutex_unlock(&ata_mutex); + PASS_RC(rc, 1, 1); + return 0; +} + +int ata_hard_reset(void) +{ + mutex_lock(&ata_mutex); + PASS_RC(ata_power_up(), 0, 0); + ata_set_active(); + mutex_unlock(&ata_mutex); + return 0; +} + +static int ata_reset(void) +{ + int rc; + mutex_lock(&ata_mutex); + if (!ata_powered) PASS_RC(ata_power_up(), 2, 0); + ata_set_active(); + rc = ata_soft_reset(); + if (IS_ERR(rc)) + { + rc = ata_hard_reset(); + if (IS_ERR(rc)) + { + rc = ERR_RC((rc << 2) | 1); + ata_power_down(); + sleep(HZ * 3); + int rc2 = ata_power_up(); + if (IS_ERR(rc2)) rc = ERR_RC((rc << 2) | 2); + } + else rc = 1; + } + ata_set_active(); + mutex_unlock(&ata_mutex); + return rc; +} + +int ata_read_sectors(IF_MD(int drive,) unsigned long start, int incount, + void* inbuf) +{ + mutex_lock(&ata_mutex); + int rc = ata_rw_sectors(start, incount, inbuf, false); + mutex_unlock(&ata_mutex); + return rc; +} + +int ata_write_sectors(IF_MD(int drive,) unsigned long start, int count, + const void* outbuf) +{ + mutex_lock(&ata_mutex); + int rc = ata_rw_sectors(start, count, (void*)((uint32_t)outbuf), true); + mutex_unlock(&ata_mutex); + return rc; +} + +void ata_spindown(int seconds) +{ + ata_sleep_timeout = seconds * HZ; +} + +void ata_sleep(void) +{ + ata_last_activity_value = current_tick - ata_sleep_timeout + HZ / 5; +} + +void ata_sleepnow(void) +{ + mutex_lock(&ata_mutex); + ata_power_down(); + mutex_unlock(&ata_mutex); +} + +void ata_close(void) +{ + ata_sleepnow(); +} + +void ata_spin(void) +{ + ata_set_active(); +} + +void ata_get_info(IF_MD(int drive,) struct storage_info *info) +{ + (*info).sector_size = SECTOR_SIZE; + (*info).num_sectors = ata_total_sectors; + (*info).vendor = "Apple"; + (*info).product = "iPod Classic"; + (*info).revision = "1.0"; +} + +long ata_last_disk_activity(void) +{ + return ata_last_activity_value; +} + +int ata_init(void) +{ + mutex_init(&ata_mutex); + semaphore_init(&ata_wakeup, 1, 0); + semaphore_init(&mmc_wakeup, 1, 0); + semaphore_init(&mmc_comp_wakeup, 1, 0); + ceata = PDAT(11) & BIT(1); + ata_powered = false; + ata_total_sectors = 0; + + /* get ata_identify_data */ + mutex_lock(&ata_mutex); + int rc = ata_power_up(); + mutex_unlock(&ata_mutex); + if (IS_ERR(rc)) return rc; + + create_thread(ata_thread, ata_stack, + sizeof(ata_stack), 0, "ATA idle monitor" + IF_PRIO(, PRIORITY_USER_INTERFACE) + IF_COP(, CPU)); + return 0; +} + +#ifdef HAVE_ATA_SMART +static int ata_smart(uint16_t* buf) +{ + if (!ata_powered) PASS_RC(ata_power_up(), 3, 0); + if (ceata) + { + memset(ceata_taskfile, 0, 16); + ceata_taskfile[0xc] = 0x4f; + ceata_taskfile[0xd] = 0xc2; + ceata_taskfile[0xe] = BIT(6); + ceata_taskfile[0xf] = 0xb0; + PASS_RC(ceata_wait_idle(), 3, 1); + if (((uint8_t*)ata_identify_data)[54] != 'A') /* Model != aAmsung */ + { + ceata_taskfile[0x9] = 0xd8; /* SMART enable operations */ + PASS_RC(ceata_write_multiple_register(0, ceata_taskfile, 16), 3, 2); + PASS_RC(ceata_check_error(), 3, 3); + } + ceata_taskfile[0x9] = 0xd0; /* SMART read data */ + PASS_RC(ceata_write_multiple_register(0, ceata_taskfile, 16), 3, 4); + PASS_RC(ceata_rw_multiple_block(false, buf, 1, CEATA_COMMAND_TIMEOUT * HZ / 1000000), 3, 5); + } + else + { + int i; + PASS_RC(ata_wait_for_not_bsy(10000000), 3, 6); + ata_write_cbr(&ATA_PIO_FED, 0xd0); + ata_write_cbr(&ATA_PIO_LMR, 0x4f); + ata_write_cbr(&ATA_PIO_LHR, 0xc2); + ata_write_cbr(&ATA_PIO_DVR, BIT(6)); + ata_write_cbr(&ATA_PIO_CSD, 0xb0); + PASS_RC(ata_wait_for_start_of_transfer(10000000), 3, 7); + for (i = 0; i < 0x100; i++) buf[i] = ata_read_cbr(&ATA_PIO_DTR); + } + ata_set_active(); + return 0; +} + +int ata_read_smart(struct ata_smart_values* smart_data) +{ + mutex_lock(&ata_mutex); + int rc = ata_smart((uint16_t*)smart_data); + mutex_unlock(&ata_mutex); + return rc; +} +#endif /* HAVE_ATA_SMART */ + +#ifdef CONFIG_STORAGE_MULTI +static int ata_num_drives(int first_drive) +{ + /* We don't care which logical drive number(s) we have been assigned */ + (void)first_drive; + + return 1; +} +#endif + +unsigned short* ata_get_identify(void) +{ + return ata_identify_data; +} + +int ata_spinup_time(void) +{ + return spinup_time; +} + +int ata_get_dma_mode(void) +{ + return dma_mode; +} + +void INT_ATA(void) +{ + uint32_t ata_irq = ATA_IRQ; + ATA_IRQ = ata_irq; + if (ata_irq & ATA_IRQ_MASK) semaphore_release(&ata_wakeup); + ATA_IRQ_MASK = 0; +} + +void INT_MMC(void) +{ + uint32_t irq = SDCI_IRQ; + if (irq & SDCI_IRQ_DAT_DONE_INT) semaphore_release(&mmc_wakeup); + if (irq & SDCI_IRQ_IOCARD_IRQ_INT) semaphore_release(&mmc_comp_wakeup); + SDCI_IRQ = irq; +} + diff --git a/firmware/target/arm/s5l8702/ipod6g/storage_ata-ipod6g.c b/firmware/target/arm/s5l8702/ipod6g/storage_ata-ipod6g.c deleted file mode 100644 index ef39a5cabb..0000000000 --- a/firmware/target/arm/s5l8702/ipod6g/storage_ata-ipod6g.c +++ /dev/null @@ -1,1131 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2007 Dave Chapman - * - * 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 "thread.h" -#include "disk.h" -#include "storage.h" -#include "timer.h" -#include "kernel.h" -#include "string.h" -#include "power.h" -#include "panic.h" -#include "mmu-arm.h" -#include "mmcdefs-target.h" -#include "s5l8702.h" -#include "led.h" -#include "ata_idle_notify.h" -#include "disk_cache.h" - - -#ifndef ATA_RETRIES -#define ATA_RETRIES 3 -#endif - - -#define CEATA_POWERUP_TIMEOUT 20000000 -#define CEATA_COMMAND_TIMEOUT 1000000 -#define CEATA_DAT_NONBUSY_TIMEOUT 5000000 -#define CEATA_MMC_RCA 1 - - -/** static, private data **/ -static uint8_t ceata_taskfile[16] STORAGE_ALIGN_ATTR; -static uint16_t ata_identify_data[0x100] STORAGE_ALIGN_ATTR; -static bool ceata; -static bool ata_lba48; -static bool ata_dma; -static uint64_t ata_total_sectors; -static struct mutex ata_mutex; -static struct semaphore ata_wakeup; -static uint32_t ata_dma_flags; -static long ata_last_activity_value = -1; -static long ata_sleep_timeout = 20 * HZ; -static uint32_t ata_stack[(DEFAULT_STACK_SIZE + 0x400) / 4]; -static bool ata_powered; -static const int ata_retries = ATA_RETRIES; -static const bool ata_error_srst = true; -static struct semaphore mmc_wakeup; -static struct semaphore mmc_comp_wakeup; -static int spinup_time = 0; -static int dma_mode = 0; -static char aligned_buffer[SECTOR_SIZE] STORAGE_ALIGN_ATTR; - -static int ata_reset(void); - - -static uint16_t ata_read_cbr(uint32_t volatile* reg) -{ - while (!(ATA_PIO_READY & 2)); - volatile uint32_t dummy __attribute__((unused)) = *reg; - while (!(ATA_PIO_READY & 1)); - return ATA_PIO_RDATA; -} - -static void ata_write_cbr(uint32_t volatile* reg, uint16_t data) -{ - while (!(ATA_PIO_READY & 2)); - *reg = data; -} - -static int ata_wait_for_not_bsy(long timeout) -{ - long startusec = USEC_TIMER; - while (true) - { - uint8_t csd = ata_read_cbr(&ATA_PIO_CSD); - if (!(csd & BIT(7))) return 0; - if (TIMEOUT_EXPIRED(startusec, timeout)) RET_ERR(0); - yield(); - } -} - -static int ata_wait_for_rdy(long timeout) -{ - long startusec = USEC_TIMER; - PASS_RC(ata_wait_for_not_bsy(timeout), 1, 0); - while (true) - { - uint8_t dad = ata_read_cbr(&ATA_PIO_DAD); - if (dad & BIT(6)) return 0; - if (TIMEOUT_EXPIRED(startusec, timeout)) RET_ERR(1); - yield(); - } -} - -static int ata_wait_for_start_of_transfer(long timeout) -{ - long startusec = USEC_TIMER; - PASS_RC(ata_wait_for_not_bsy(timeout), 2, 0); - while (true) - { - uint8_t dad = ata_read_cbr(&ATA_PIO_DAD); - if (dad & BIT(0)) RET_ERR(1); - if ((dad & (BIT(7) | BIT(3))) == BIT(3)) return 0; - if (TIMEOUT_EXPIRED(startusec, timeout)) RET_ERR(2); - yield(); - } -} - -static int ata_wait_for_end_of_transfer(long timeout) -{ - PASS_RC(ata_wait_for_not_bsy(timeout), 2, 0); - uint8_t dad = ata_read_cbr(&ATA_PIO_DAD); - if (dad & BIT(0)) RET_ERR(1); - if ((dad & (BIT(3) | BITRANGE(5, 7))) == BIT(6)) return 0; - RET_ERR(2); -} - -static int mmc_dsta_check_command_success(bool disable_crc) -{ - int rc = 0; - uint32_t dsta = SDCI_DSTA; - if (dsta & SDCI_DSTA_RESTOUTE) rc |= 1; - if (dsta & SDCI_DSTA_RESENDE) rc |= 2; - if (dsta & SDCI_DSTA_RESINDE) rc |= 4; - if (!disable_crc) - if (dsta & SDCI_DSTA_RESCRCE) - rc |= 8; - if (rc) RET_ERR(rc); - return 0; -} - -static bool mmc_send_command(uint32_t cmd, uint32_t arg, uint32_t* result, int timeout) -{ - long starttime = USEC_TIMER; - while ((SDCI_STATE & SDCI_STATE_CMD_STATE_MASK) != SDCI_STATE_CMD_STATE_CMD_IDLE) - { - if (TIMEOUT_EXPIRED(starttime, timeout)) RET_ERR(0); - yield(); - } - SDCI_STAC = SDCI_STAC_CLR_CMDEND | SDCI_STAC_CLR_BIT_3 - | SDCI_STAC_CLR_RESEND | SDCI_STAC_CLR_DATEND - | SDCI_STAC_CLR_DAT_CRCEND | SDCI_STAC_CLR_CRC_STAEND - | SDCI_STAC_CLR_RESTOUTE | SDCI_STAC_CLR_RESENDE - | SDCI_STAC_CLR_RESINDE | SDCI_STAC_CLR_RESCRCE - | SDCI_STAC_CLR_WR_DATCRCE | SDCI_STAC_CLR_RD_DATCRCE - | SDCI_STAC_CLR_RD_DATENDE0 | SDCI_STAC_CLR_RD_DATENDE1 - | SDCI_STAC_CLR_RD_DATENDE2 | SDCI_STAC_CLR_RD_DATENDE3 - | SDCI_STAC_CLR_RD_DATENDE4 | SDCI_STAC_CLR_RD_DATENDE5 - | SDCI_STAC_CLR_RD_DATENDE6 | SDCI_STAC_CLR_RD_DATENDE7; - SDCI_ARGU = arg; - SDCI_CMD = cmd; - if (!(SDCI_DSTA & SDCI_DSTA_CMDRDY)) RET_ERR(1); - SDCI_CMD = cmd | SDCI_CMD_CMDSTR; - long sleepbase = USEC_TIMER; - while (TIMEOUT_EXPIRED(sleepbase, 1000)) yield(); - while (!(SDCI_DSTA & SDCI_DSTA_CMDEND)) - { - if (TIMEOUT_EXPIRED(starttime, timeout)) RET_ERR(2); - yield(); - } - if ((cmd & SDCI_CMD_RES_TYPE_MASK) != SDCI_CMD_RES_TYPE_NONE) - { - while (!(SDCI_DSTA & SDCI_DSTA_RESEND)) - { - if (TIMEOUT_EXPIRED(starttime, timeout)) RET_ERR(3); - yield(); - } - if (cmd & SDCI_CMD_RES_BUSY) - while (SDCI_DSTA & SDCI_DSTA_DAT_BUSY) - { - if (TIMEOUT_EXPIRED(starttime, CEATA_DAT_NONBUSY_TIMEOUT)) RET_ERR(4); - yield(); - } - } - bool nocrc = (cmd & SDCI_CMD_RES_SIZE_MASK) == SDCI_CMD_RES_SIZE_136; - PASS_RC(mmc_dsta_check_command_success(nocrc), 3, 5); - if (result) *result = SDCI_RESP0; - return 0; -} - -static int mmc_get_card_status(uint32_t* result) -{ - return mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_SEND_STATUS) - | SDCI_CMD_CMD_TYPE_AC | SDCI_CMD_RES_TYPE_R1 - | SDCI_CMD_RES_SIZE_48 | SDCI_CMD_NCR_NID_NCR, - MMC_CMD_SEND_STATUS_RCA(CEATA_MMC_RCA), result, CEATA_COMMAND_TIMEOUT); -} - -static int mmc_init(void) -{ - sleep(HZ / 10); - PASS_RC(mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_GO_IDLE_STATE) - | SDCI_CMD_CMD_TYPE_BC | SDCI_CMD_RES_TYPE_NONE - | SDCI_CMD_RES_SIZE_48 | SDCI_CMD_NCR_NID_NID, - 0, NULL, CEATA_COMMAND_TIMEOUT), 3, 0); - long startusec = USEC_TIMER; - uint32_t result; - do - { - if (TIMEOUT_EXPIRED(startusec, CEATA_POWERUP_TIMEOUT)) RET_ERR(1); - sleep(HZ / 100); - PASS_RC(mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_SEND_OP_COND) - | SDCI_CMD_CMD_TYPE_BCR | SDCI_CMD_RES_TYPE_R3 - | SDCI_CMD_RES_SIZE_48 | SDCI_CMD_NCR_NID_NID, - MMC_CMD_SEND_OP_COND_OCR(MMC_OCR_270_360), - NULL, CEATA_COMMAND_TIMEOUT), 3, 2); - result = SDCI_RESP0; - } - while (!(result & MMC_OCR_POWER_UP_DONE)); - PASS_RC(mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_ALL_SEND_CID) - | SDCI_CMD_CMD_TYPE_BCR | SDCI_CMD_RES_TYPE_R2 - | SDCI_CMD_RES_SIZE_136 | SDCI_CMD_NCR_NID_NID, - 0, NULL, CEATA_COMMAND_TIMEOUT), 3, 3); - PASS_RC(mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_SET_RELATIVE_ADDR) - | SDCI_CMD_CMD_TYPE_BCR | SDCI_CMD_RES_TYPE_R1 - | SDCI_CMD_RES_SIZE_48 | SDCI_CMD_NCR_NID_NCR, - MMC_CMD_SET_RELATIVE_ADDR_RCA(CEATA_MMC_RCA), - NULL, CEATA_COMMAND_TIMEOUT), 3, 4); - PASS_RC(mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_SELECT_CARD) - | SDCI_CMD_CMD_TYPE_AC | SDCI_CMD_RES_TYPE_R1 - | SDCI_CMD_RES_SIZE_48 | SDCI_CMD_NCR_NID_NCR, - MMC_CMD_SELECT_CARD_RCA(CEATA_MMC_RCA), - NULL, CEATA_COMMAND_TIMEOUT), 3, 5); - PASS_RC(mmc_get_card_status(&result), 3, 6); - if ((result & MMC_STATUS_CURRENT_STATE_MASK) != MMC_STATUS_CURRENT_STATE_TRAN) RET_ERR(7); - return 0; -} - -static int mmc_fastio_write(uint32_t addr, uint32_t data) -{ - return mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_FAST_IO) - | SDCI_CMD_CMD_TYPE_AC | SDCI_CMD_RES_TYPE_R4 - | SDCI_CMD_RES_SIZE_48 | SDCI_CMD_NCR_NID_NCR, - MMC_CMD_FAST_IO_RCA(CEATA_MMC_RCA) | MMC_CMD_FAST_IO_DIRECTION_WRITE - | MMC_CMD_FAST_IO_ADDRESS(addr) | MMC_CMD_FAST_IO_DATA(data), - NULL, CEATA_COMMAND_TIMEOUT); -} - -static int mmc_fastio_read(uint32_t addr, uint32_t* data) -{ - return mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_FAST_IO) - | SDCI_CMD_CMD_TYPE_AC | SDCI_CMD_RES_TYPE_R4 - | SDCI_CMD_RES_SIZE_48 | SDCI_CMD_NCR_NID_NCR, - MMC_CMD_FAST_IO_RCA(CEATA_MMC_RCA) | MMC_CMD_FAST_IO_DIRECTION_READ - | MMC_CMD_FAST_IO_ADDRESS(addr), data, CEATA_COMMAND_TIMEOUT); -} - -static int ceata_soft_reset(void) -{ - PASS_RC(mmc_fastio_write(6, 4), 2, 0); - sleep(HZ / 100); - PASS_RC(mmc_fastio_write(6, 0), 2, 1); - sleep(HZ / 100); - long startusec = USEC_TIMER; - uint32_t status; - do - { - PASS_RC(mmc_fastio_read(0xf, &status), 2, 2); - if (TIMEOUT_EXPIRED(startusec, CEATA_POWERUP_TIMEOUT)) RET_ERR(3); - sleep(HZ / 100); - } - while (status & 0x80); - return 0; -} - -static int mmc_dsta_check_data_success(void) -{ - int rc = 0; - uint32_t dsta = SDCI_DSTA; - if (dsta & (SDCI_DSTA_WR_DATCRCE | SDCI_DSTA_RD_DATCRCE)) - { - if (dsta & SDCI_DSTA_WR_DATCRCE) rc |= 1; - if (dsta & SDCI_DSTA_RD_DATCRCE) rc |= 2; - if ((dsta & SDCI_DSTA_WR_CRC_STATUS_MASK) == SDCI_DSTA_WR_CRC_STATUS_TXERR) rc |= 4; - else if ((dsta & SDCI_DSTA_WR_CRC_STATUS_MASK) == SDCI_DSTA_WR_CRC_STATUS_CARDERR) rc |= 8; - } - if (dsta & (SDCI_DSTA_RD_DATENDE0 | SDCI_DSTA_RD_DATENDE1 | SDCI_DSTA_RD_DATENDE2 - | SDCI_DSTA_RD_DATENDE3 | SDCI_DSTA_RD_DATENDE4 | SDCI_DSTA_RD_DATENDE5 - | SDCI_DSTA_RD_DATENDE6 | SDCI_DSTA_RD_DATENDE7)) - rc |= 16; - if (rc) RET_ERR(rc); - return 0; -} - -static void mmc_discard_irq(void) -{ - SDCI_IRQ = SDCI_IRQ_DAT_DONE_INT | SDCI_IRQ_MASK_MASK_IOCARD_IRQ_INT - | SDCI_IRQ_MASK_MASK_READ_WAIT_INT; - semaphore_wait(&mmc_wakeup, 0); -} - -static int ceata_read_multiple_register(uint32_t addr, void* dest, uint32_t size) -{ - if (size > 0x10) RET_ERR(0); - mmc_discard_irq(); - SDCI_DMASIZE = size; - SDCI_DMACOUNT = 1; - SDCI_DMAADDR = dest; - SDCI_DCTRL = SDCI_DCTRL_TXFIFORST | SDCI_DCTRL_RXFIFORST; - commit_discard_dcache(); - PASS_RC(mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_CEATA_RW_MULTIPLE_REG) - | SDCI_CMD_CMD_TYPE_ADTC | SDCI_CMD_RES_TYPE_R1 - | SDCI_CMD_RES_SIZE_48 | SDCI_CMD_NCR_NID_NCR, - MMC_CMD_CEATA_RW_MULTIPLE_REG_DIRECTION_READ - | MMC_CMD_CEATA_RW_MULTIPLE_REG_ADDRESS(addr & 0xfc) - | MMC_CMD_CEATA_RW_MULTIPLE_REG_COUNT(size & 0xfc), - NULL, CEATA_COMMAND_TIMEOUT), 2, 1); - if (semaphore_wait(&mmc_wakeup, CEATA_COMMAND_TIMEOUT * HZ / 1000000) - == OBJ_WAIT_TIMEDOUT) RET_ERR(2); - PASS_RC(mmc_dsta_check_data_success(), 2, 3); - return 0; -} - -static int ceata_write_multiple_register(uint32_t addr, void* dest, uint32_t size) -{ - uint32_t i; - if (size > 0x10) RET_ERR(0); - mmc_discard_irq(); - SDCI_DMASIZE = size; - SDCI_DMACOUNT = 0; - SDCI_DCTRL = SDCI_DCTRL_TXFIFORST | SDCI_DCTRL_RXFIFORST; - PASS_RC(mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_CEATA_RW_MULTIPLE_REG) - | SDCI_CMD_CMD_TYPE_ADTC | SDCI_CMD_CMD_RD_WR - | SDCI_CMD_RES_BUSY | SDCI_CMD_RES_TYPE_R1 - | SDCI_CMD_RES_SIZE_48 | SDCI_CMD_NCR_NID_NCR, - MMC_CMD_CEATA_RW_MULTIPLE_REG_DIRECTION_WRITE - | MMC_CMD_CEATA_RW_MULTIPLE_REG_ADDRESS(addr & 0xfc) - | MMC_CMD_CEATA_RW_MULTIPLE_REG_COUNT(size & 0xfc), - NULL, CEATA_COMMAND_TIMEOUT), 3, 1); - SDCI_DCTRL = SDCI_DCTRL_TRCONT_TX; - for (i = 0; i < size / 4; i++) SDCI_DATA = ((uint32_t*)dest)[i]; - long startusec = USEC_TIMER; - if (semaphore_wait(&mmc_wakeup, CEATA_COMMAND_TIMEOUT * HZ / 1000000) - == OBJ_WAIT_TIMEDOUT) RET_ERR(2); - while ((SDCI_STATE & SDCI_STATE_DAT_STATE_MASK) != SDCI_STATE_DAT_STATE_IDLE) - { - if (TIMEOUT_EXPIRED(startusec, CEATA_COMMAND_TIMEOUT)) RET_ERR(3); - yield(); - } - PASS_RC(mmc_dsta_check_data_success(), 3, 4); - return 0; -} - -static int ceata_init(int buswidth) -{ - uint32_t result; - PASS_RC(mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_SWITCH) | SDCI_CMD_RES_BUSY - | SDCI_CMD_CMD_TYPE_AC | SDCI_CMD_RES_TYPE_R1 - | SDCI_CMD_RES_SIZE_48 | SDCI_CMD_NCR_NID_NCR, - MMC_CMD_SWITCH_ACCESS_WRITE_BYTE - | MMC_CMD_SWITCH_INDEX(MMC_CMD_SWITCH_FIELD_HS_TIMING) - | MMC_CMD_SWITCH_VALUE(MMC_CMD_SWITCH_FIELD_HS_TIMING_HIGH_SPEED), - &result, CEATA_COMMAND_TIMEOUT), 3, 0); - if (result & MMC_STATUS_SWITCH_ERROR) RET_ERR(1); - if (buswidth > 1) - { - int setting; - if (buswidth == 4) setting = MMC_CMD_SWITCH_FIELD_BUS_WIDTH_4BIT; - else if (buswidth == 8) setting = MMC_CMD_SWITCH_FIELD_BUS_WIDTH_8BIT; - else setting = MMC_CMD_SWITCH_FIELD_BUS_WIDTH_1BIT; - PASS_RC(mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_SWITCH) | SDCI_CMD_RES_BUSY - | SDCI_CMD_CMD_TYPE_AC | SDCI_CMD_RES_TYPE_R1 - | SDCI_CMD_RES_SIZE_48 | SDCI_CMD_NCR_NID_NCR, - MMC_CMD_SWITCH_ACCESS_WRITE_BYTE - | MMC_CMD_SWITCH_INDEX(MMC_CMD_SWITCH_FIELD_BUS_WIDTH) - | MMC_CMD_SWITCH_VALUE(setting), - &result, CEATA_COMMAND_TIMEOUT), 3, 2); - if (result & MMC_STATUS_SWITCH_ERROR) RET_ERR(3); - if (buswidth == 4) - SDCI_CTRL = (SDCI_CTRL & ~SDCI_CTRL_BUS_WIDTH_MASK) | SDCI_CTRL_BUS_WIDTH_4BIT; - else if (buswidth == 8) - SDCI_CTRL = (SDCI_CTRL & ~SDCI_CTRL_BUS_WIDTH_MASK) | SDCI_CTRL_BUS_WIDTH_8BIT; - } - PASS_RC(ceata_soft_reset(), 3, 4); - PASS_RC(ceata_read_multiple_register(0, ceata_taskfile, 0x10), 3, 5); - if (ceata_taskfile[0xc] != 0xce || ceata_taskfile[0xd] != 0xaa) RET_ERR(6); - PASS_RC(mmc_fastio_write(6, 0), 3, 7); - return 0; -} - -static int ceata_check_error(void) -{ - uint32_t status, error; - PASS_RC(mmc_fastio_read(0xf, &status), 2, 0); - if (status & 1) - { - PASS_RC(mmc_fastio_read(0x9, &error), 2, 1); - RET_ERR((error << 2) | 2); - } - return 0; -} - -static int ceata_wait_idle(void) -{ - long startusec = USEC_TIMER; - while (true) - { - uint32_t status; - PASS_RC(mmc_fastio_read(0xf, &status), 1, 0); - if (!(status & 0x88)) return 0; - if (TIMEOUT_EXPIRED(startusec, CEATA_DAT_NONBUSY_TIMEOUT)) RET_ERR(1); - sleep(HZ / 20); - } -} - -static int ceata_cancel_command(void) -{ - *((uint32_t volatile*)0x3cf00200) = 0x9000e; - udelay(1); - *((uint32_t volatile*)0x3cf00200) = 0x9000f; - udelay(1); - *((uint32_t volatile*)0x3cf00200) = 0x90003; - udelay(1); - PASS_RC(mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_STOP_TRANSMISSION) - | SDCI_CMD_CMD_TYPE_AC | SDCI_CMD_RES_TYPE_R1 | SDCI_CMD_RES_BUSY - | SDCI_CMD_RES_SIZE_48 | SDCI_CMD_NCR_NID_NCR, - 0, NULL, CEATA_COMMAND_TIMEOUT), 1, 0); - PASS_RC(ceata_wait_idle(), 1, 1); - return 0; -} - -static int ceata_rw_multiple_block(bool write, void* buf, uint32_t count, long timeout) -{ - mmc_discard_irq(); - uint32_t responsetype; - uint32_t cmdtype; - uint32_t direction; - if (write) - { - cmdtype = SDCI_CMD_CMD_TYPE_ADTC | SDCI_CMD_CMD_RD_WR; - responsetype = SDCI_CMD_RES_TYPE_R1 | SDCI_CMD_RES_BUSY; - direction = MMC_CMD_CEATA_RW_MULTIPLE_BLOCK_DIRECTION_WRITE; - } - else - { - cmdtype = SDCI_CMD_CMD_TYPE_ADTC; - responsetype = SDCI_CMD_RES_TYPE_R1; - direction = MMC_CMD_CEATA_RW_MULTIPLE_BLOCK_DIRECTION_READ; - } - SDCI_DMASIZE = 0x200; - SDCI_DMAADDR = buf; - SDCI_DMACOUNT = count; - SDCI_DCTRL = SDCI_DCTRL_TXFIFORST | SDCI_DCTRL_RXFIFORST; - commit_discard_dcache(); - PASS_RC(mmc_send_command(SDCI_CMD_CMD_NUM(MMC_CMD_CEATA_RW_MULTIPLE_BLOCK) - | SDCI_CMD_CMD_TYPE_ADTC | cmdtype | responsetype - | SDCI_CMD_RES_SIZE_48 | SDCI_CMD_NCR_NID_NCR, - direction | MMC_CMD_CEATA_RW_MULTIPLE_BLOCK_COUNT(count), - NULL, CEATA_COMMAND_TIMEOUT), 3, 0); - if (write) SDCI_DCTRL = SDCI_DCTRL_TRCONT_TX; - if (semaphore_wait(&mmc_wakeup, timeout) == OBJ_WAIT_TIMEDOUT) - { - PASS_RC(ceata_cancel_command(), 3, 1); - RET_ERR(2); - } - PASS_RC(mmc_dsta_check_data_success(), 3, 3); - if (semaphore_wait(&mmc_comp_wakeup, timeout) == OBJ_WAIT_TIMEDOUT) - { - PASS_RC(ceata_cancel_command(), 3, 4); - RET_ERR(5); - } - PASS_RC(ceata_check_error(), 3, 6); - return 0; -} - -static int ata_identify(uint16_t* buf) -{ - int i; - if (ceata) - { - memset(ceata_taskfile, 0, 16); - ceata_taskfile[0xf] = 0xec; - PASS_RC(ceata_wait_idle(), 2, 0); - PASS_RC(ceata_write_multiple_register(0, ceata_taskfile, 16), 2, 1); - PASS_RC(ceata_rw_multiple_block(false, buf, 1, CEATA_COMMAND_TIMEOUT * HZ / 1000000), 2, 2); - } - else - { - PASS_RC(ata_wait_for_not_bsy(10000000), 1, 0); - ata_write_cbr(&ATA_PIO_DVR, 0); - ata_write_cbr(&ATA_PIO_CSD, 0xec); - PASS_RC(ata_wait_for_start_of_transfer(10000000), 1, 1); - for (i = 0; i < 0x100; i++) buf[i] = ata_read_cbr(&ATA_PIO_DTR); - } - return 0; -} - -static void ata_set_active(void) -{ - ata_last_activity_value = current_tick; -} - -bool ata_disk_is_active(void) -{ - return ata_powered; -} - -static int ata_set_feature(uint32_t feature, uint32_t param) -{ - if (ceata) - { - memset(ceata_taskfile, 0, 16); - ceata_taskfile[0x1] = feature; - ceata_taskfile[0x2] = param; - ceata_taskfile[0xf] = 0xef; - PASS_RC(ceata_wait_idle(), 2, 0); - PASS_RC(ceata_write_multiple_register(0, ceata_taskfile, 16), 2, 1); - PASS_RC(ceata_wait_idle(), 2, 2); - } - else - { - PASS_RC(ata_wait_for_rdy(2000000), 2, 0); - ata_write_cbr(&ATA_PIO_DVR, 0); - ata_write_cbr(&ATA_PIO_FED, feature); - ata_write_cbr(&ATA_PIO_SCR, param); - ata_write_cbr(&ATA_PIO_CSD, 0xef); - PASS_RC(ata_wait_for_rdy(2000000), 2, 1); - } - return 0; -} - -/* - * ATA_UDMA_TIME register is documented on s3c6400 datasheet, information - * included in s5l8700 datasheet is wrong or not valid for s5l8702. - * - * On Classic (boosted): - * HClk = 108 MHz. -> T = ~9.26 ns. - * - * Configured values (in nanoseconds): - * - * UDMA ATA_UDMA_TIME tACK tRP tSS tDVS tDVH Tcyc WR(MB/s) - * 0 0x4071152 27.8 166.7 55.6 74.1 46.3 120.4 16.6 - * 1 0x2050d52 27.8 129.6 55.6 55.6 27.8 83.4 24 - * 2 0x2030a52 27.8 101.8 55.6 37 27.8 64.8 30.9 - * 3 0x1020a52 27.8 101.8 55.6 27.8 18.5 46.3 43.2 - * 4 0x1010a52 27.8 101.8 55.6 18.5 18.5 37 54 - * - * Tcyc = tDVS+tDVH - * WR[bytes/s] = 1/Tcyc[s] * 2[bytes] - */ -static int ata_power_up(void) -{ - ata_set_active(); - ide_power_enable(true); - long spinup_start = current_tick; - if (ceata) - { - ata_lba48 = true; - ata_dma = true; - PCON(8) = 0x33333333; - PCON(9) = 0x00000033; - PCON(11) |= 0xf; - *((uint32_t volatile*)0x38a00000) = 0; - *((uint32_t volatile*)0x38700000) = 0; - PWRCON(0) &= ~(1 << 9); - SDCI_RESET = 0xa5; - sleep(HZ / 100); - *((uint32_t volatile*)0x3cf00380) = 0; - *((uint32_t volatile*)0x3cf0010c) = 0xff; - SDCI_CTRL = SDCI_CTRL_SDCIEN | SDCI_CTRL_CLK_SEL_SDCLK - | SDCI_CTRL_BIT_8 | SDCI_CTRL_BIT_14; - SDCI_CDIV = SDCI_CDIV_CLKDIV(260); - *((uint32_t volatile*)0x3cf00200) = 0xb000f; - SDCI_IRQ_MASK = SDCI_IRQ_MASK_MASK_DAT_DONE_INT | SDCI_IRQ_MASK_MASK_IOCARD_IRQ_INT; - PASS_RC(mmc_init(), 3, 0); - SDCI_CDIV = SDCI_CDIV_CLKDIV(4); - sleep(HZ / 100); - PASS_RC(ceata_init(8), 3, 1); - PASS_RC(ata_identify(ata_identify_data), 3, 2); - dma_mode = 0x44; - } - else - { - PCON(7) = 0x44444444; - PCON(8) = 0x44444444; - PCON(9) = 0x44444444; - PCON(10) = (PCON(10) & ~0xffff) | 0x4444; - PWRCON(0) &= ~(1 << 5); - ATA_CFG = BIT(0); - sleep(HZ / 100); - ATA_CFG = 0; - sleep(HZ / 100); - ATA_SWRST = BIT(0); - sleep(HZ / 100); - ATA_SWRST = 0; - sleep(HZ / 10); - ATA_CONTROL = BIT(0); - sleep(HZ / 5); - ATA_PIO_TIME = 0x191f7; - ATA_PIO_LHR = 0; - ATA_CFG = BIT(6); - while (!(ATA_PIO_READY & BIT(1))) yield(); - PASS_RC(ata_identify(ata_identify_data), 3, 3); - uint32_t piotime = 0x11f3; - uint32_t mdmatime = 0x1c175; - uint32_t udmatime = 0x4071152; - uint32_t param = 0; - ata_dma_flags = 0; - ata_lba48 = ata_identify_data[83] & BIT(10) ? true : false; - if (ata_identify_data[53] & BIT(1)) - { - if (ata_identify_data[64] & BIT(1)) piotime = 0x2072; - else if (ata_identify_data[64] & BIT(0)) piotime = 0x7083; - } - if (ata_identify_data[63] & BIT(2)) - { - mdmatime = 0x5072; - param = 0x22; - } - else if (ata_identify_data[63] & BIT(1)) - { - mdmatime = 0x7083; - param = 0x21; - } - if (ata_identify_data[63] & BITRANGE(0, 2)) - { - ata_dma_flags = BIT(3) | BIT(10); - param |= 0x20; - } - if (ata_identify_data[53] & BIT(2)) - { - if (ata_identify_data[88] & BIT(4)) - { - udmatime = 0x1010a52; - param = 0x44; - } - else if (ata_identify_data[88] & BIT(3)) - { - udmatime = 0x1020a52; - param = 0x43; - } - else if (ata_identify_data[88] & BIT(2)) - { - udmatime = 0x2030a52; - param = 0x42; - } - else if (ata_identify_data[88] & BIT(1)) - { - udmatime = 0x2050d52; - param = 0x41; - } - else if (ata_identify_data[88] & BIT(0)) - { - param = 0x40; - } - if (ata_identify_data[88] & BITRANGE(0, 4)) - { - ata_dma_flags = BIT(2) | BIT(3) | BIT(9) | BIT(10); - } - } - ata_dma = param ? true : false; - dma_mode = param; - PASS_RC(ata_set_feature(0x03, param), 3, 4); - if (ata_identify_data[82] & BIT(5)) - PASS_RC(ata_set_feature(0x02, 0), 3, 5); - if (ata_identify_data[82] & BIT(6)) PASS_RC(ata_set_feature(0xaa, 0), 3, 6); - ATA_PIO_TIME = piotime; - ATA_MDMA_TIME = mdmatime; - ATA_UDMA_TIME = udmatime; - } - spinup_time = current_tick - spinup_start; - if (ata_lba48) - ata_total_sectors = ata_identify_data[100] - | (((uint64_t)ata_identify_data[101]) << 16) - | (((uint64_t)ata_identify_data[102]) << 32) - | (((uint64_t)ata_identify_data[103]) << 48); - else ata_total_sectors = ata_identify_data[60] | (((uint32_t)ata_identify_data[61]) << 16); - ata_total_sectors >>= 3; - ata_powered = true; - ata_set_active(); - return 0; -} - -static void ata_power_down(void) -{ - if (!ata_powered) return; - if (ceata) - { - memset(ceata_taskfile, 0, 16); - ceata_taskfile[0xf] = 0xe0; - ceata_wait_idle(); - ceata_write_multiple_register(0, ceata_taskfile, 16); - ceata_wait_idle(); - sleep(HZ); - PWRCON(0) |= (1 << 9); - } - else - { - ata_wait_for_rdy(1000000); - ata_write_cbr(&ATA_PIO_DVR, 0); - ata_write_cbr(&ATA_PIO_CSD, 0xe0); - ata_wait_for_rdy(1000000); - sleep(HZ / 30); - ATA_CONTROL = 0; - while (!(ATA_CONTROL & BIT(1))) yield(); - PWRCON(0) |= (1 << 5); - } - PCON(7) = 0; - PCON(8) = 0; - PCON(9) = 0; - PCON(10) &= ~0xffff; - PCON(11) &= ~0xf; - ide_power_enable(false); - ata_powered = false; -} - -static int ata_rw_chunk_internal(uint64_t sector, uint32_t cnt, void* buffer, bool write) -{ - if (ceata) - { - memset(ceata_taskfile, 0, 16); - ceata_taskfile[0x2] = cnt >> 5; - ceata_taskfile[0x3] = sector >> 21; - ceata_taskfile[0x4] = sector >> 29; - ceata_taskfile[0x5] = sector >> 37; - ceata_taskfile[0xa] = cnt << 3; - ceata_taskfile[0xb] = sector << 3; - ceata_taskfile[0xc] = sector >> 5; - ceata_taskfile[0xd] = sector >> 13; - ceata_taskfile[0xf] = write ? 0x35 : 0x25; - PASS_RC(ceata_wait_idle(), 2, 0); - PASS_RC(ceata_write_multiple_register(0, ceata_taskfile, 16), 2, 1); - PASS_RC(ceata_rw_multiple_block(write, buffer, cnt << 3, CEATA_COMMAND_TIMEOUT * HZ / 1000000), 2, 2); - } - else - { - PASS_RC(ata_wait_for_rdy(100000), 2, 0); - ata_write_cbr(&ATA_PIO_DVR, 0); - if (ata_lba48) - { - ata_write_cbr(&ATA_PIO_SCR, cnt >> 5); - ata_write_cbr(&ATA_PIO_SCR, (cnt << 3) & 0xff); - ata_write_cbr(&ATA_PIO_LHR, (sector >> 37) & 0xff); - ata_write_cbr(&ATA_PIO_LMR, (sector >> 29) & 0xff); - ata_write_cbr(&ATA_PIO_LLR, (sector >> 21) & 0xff); - ata_write_cbr(&ATA_PIO_LHR, (sector >> 13) & 0xff); - ata_write_cbr(&ATA_PIO_LMR, (sector >> 5) & 0xff); - ata_write_cbr(&ATA_PIO_LLR, (sector << 3) & 0xff); - ata_write_cbr(&ATA_PIO_DVR, BIT(6)); - if (write) ata_write_cbr(&ATA_PIO_CSD, ata_dma ? 0x35 : 0x39); - else ata_write_cbr(&ATA_PIO_CSD, ata_dma ? 0x25 : 0x29); - } - else - { - ata_write_cbr(&ATA_PIO_SCR, (cnt << 3) & 0xff); - ata_write_cbr(&ATA_PIO_LHR, (sector >> 13) & 0xff); - ata_write_cbr(&ATA_PIO_LMR, (sector >> 5) & 0xff); - ata_write_cbr(&ATA_PIO_LLR, (sector << 3) & 0xff); - ata_write_cbr(&ATA_PIO_DVR, BIT(6) | ((sector >> 21) & 0xf)); - if (write) ata_write_cbr(&ATA_PIO_CSD, ata_dma ? 0xca : 0x30); - else ata_write_cbr(&ATA_PIO_CSD, ata_dma ? 0xc8 : 0xc4); - } - if (ata_dma) - { - PASS_RC(ata_wait_for_start_of_transfer(500000), 2, 1); - if (write) - { - ATA_SBUF_START = buffer; - ATA_SBUF_SIZE = SECTOR_SIZE * cnt; - ATA_CFG |= BIT(4); - } - else - { - ATA_TBUF_START = buffer; - ATA_TBUF_SIZE = SECTOR_SIZE * cnt; - ATA_CFG &= ~BIT(4); - } - ATA_XFR_NUM = SECTOR_SIZE * cnt - 1; - ATA_CFG |= ata_dma_flags; - ATA_CFG &= ~(BIT(7) | BIT(8)); - semaphore_wait(&ata_wakeup, 0); - ATA_IRQ = BITRANGE(0, 4); - ATA_IRQ_MASK = BIT(0); - ATA_COMMAND = BIT(0); - if (semaphore_wait(&ata_wakeup, 500000 * HZ / 1000000) - == OBJ_WAIT_TIMEDOUT) - { - ATA_COMMAND = BIT(1); - ATA_CFG &= ~(BITRANGE(2, 3) | BIT(12)); - RET_ERR(2); - } - ATA_COMMAND = BIT(1); - ATA_CFG &= ~(BITRANGE(2, 3) | BIT(12)); - } - else - { - cnt *= SECTOR_SIZE / 512; - while (cnt--) - { - int i; - PASS_RC(ata_wait_for_start_of_transfer(500000), 2, 1); - if (write) - for (i = 0; i < 256; i++) - ata_write_cbr(&ATA_PIO_DTR, ((uint16_t*)buffer)[i]); - else - for (i = 0; i < 256; i++) - ((uint16_t*)buffer)[i] = ata_read_cbr(&ATA_PIO_DTR); - buffer += 512; - } - } - PASS_RC(ata_wait_for_end_of_transfer(100000), 2, 3); - } - return 0; -} - -static int ata_rw_chunk(uint64_t sector, uint32_t cnt, void* buffer, bool write) -{ - led(true); - int rc = ata_rw_chunk_internal(sector, cnt, buffer, write); - led(false); - return rc; -} - -static int ata_rw_sectors(uint64_t sector, uint32_t count, void* buffer, bool write) -{ - if (STORAGE_OVERLAP((uint32_t)buffer)) - { - while (count) - { - if (write) - memcpy(aligned_buffer, buffer, SECTOR_SIZE); - - PASS_RC(ata_rw_sectors(sector, 1, aligned_buffer, write), 0, 0); - - if (!write) - memcpy(buffer, aligned_buffer, SECTOR_SIZE); - - buffer += SECTOR_SIZE; - sector++; - count--; - } - - return 0; - } - - if (!ata_powered) ata_power_up(); - if (sector + count > ata_total_sectors) RET_ERR(0); - ata_set_active(); - if (ata_dma && write) commit_dcache(); - else if (ata_dma) commit_discard_dcache(); - if (!ceata) ATA_COMMAND = BIT(1); - while (count) - { - uint32_t cnt = MIN(ata_lba48 ? 8192 : 32, count); - int rc = -1; - rc = ata_rw_chunk(sector, cnt, buffer, write); - if (rc && ata_error_srst) ata_reset(); - if (rc && ata_retries) - { - void* buf = buffer; - uint64_t sect; - for (sect = sector; sect < sector + cnt; sect++) - { - rc = -1; - int tries = ata_retries; - while (tries-- && rc) - { - rc = ata_rw_chunk(sect, 1, buf, write); - if (rc && ata_error_srst) ata_reset(); - } - if (rc) break; - buf += SECTOR_SIZE; - } - } - PASS_RC(rc, 1, 1); - buffer += SECTOR_SIZE * cnt; - sector += cnt; - count -= cnt; - } - ata_set_active(); - return 0; -} - -static void ata_thread(void) -{ - while (true) - { - mutex_lock(&ata_mutex); - if (TIME_AFTER(current_tick, ata_last_activity_value + ata_sleep_timeout) && ata_powered) - { - call_storage_idle_notifys(false); - ata_power_down(); - } - mutex_unlock(&ata_mutex); - sleep(HZ / 2); - } -} - -/* API Functions */ -int ata_soft_reset(void) -{ - int rc; - mutex_lock(&ata_mutex); - if (!ata_powered) PASS_RC(ata_power_up(), 1, 0); - ata_set_active(); - if (ceata) rc = ceata_soft_reset(); - else - { - ata_write_cbr(&ATA_PIO_DAD, BIT(1) | BIT(2)); - udelay(10); - ata_write_cbr(&ATA_PIO_DAD, 0); - rc = ata_wait_for_rdy(3000000); - } - ata_set_active(); - mutex_unlock(&ata_mutex); - PASS_RC(rc, 1, 1); - return 0; -} - -int ata_hard_reset(void) -{ - mutex_lock(&ata_mutex); - PASS_RC(ata_power_up(), 0, 0); - ata_set_active(); - mutex_unlock(&ata_mutex); - return 0; -} - -static int ata_reset(void) -{ - int rc; - mutex_lock(&ata_mutex); - if (!ata_powered) PASS_RC(ata_power_up(), 2, 0); - ata_set_active(); - rc = ata_soft_reset(); - if (IS_ERR(rc)) - { - rc = ata_hard_reset(); - if (IS_ERR(rc)) - { - rc = ERR_RC((rc << 2) | 1); - ata_power_down(); - sleep(HZ * 3); - int rc2 = ata_power_up(); - if (IS_ERR(rc2)) rc = ERR_RC((rc << 2) | 2); - } - else rc = 1; - } - ata_set_active(); - mutex_unlock(&ata_mutex); - return rc; -} - -int ata_read_sectors(IF_MD(int drive,) unsigned long start, int incount, - void* inbuf) -{ - mutex_lock(&ata_mutex); - int rc = ata_rw_sectors(start, incount, inbuf, false); - mutex_unlock(&ata_mutex); - return rc; -} - -int ata_write_sectors(IF_MD(int drive,) unsigned long start, int count, - const void* outbuf) -{ - mutex_lock(&ata_mutex); - int rc = ata_rw_sectors(start, count, (void*)((uint32_t)outbuf), true); - mutex_unlock(&ata_mutex); - return rc; -} - -void ata_spindown(int seconds) -{ - ata_sleep_timeout = seconds * HZ; -} - -void ata_sleep(void) -{ - ata_last_activity_value = current_tick - ata_sleep_timeout + HZ / 5; -} - -void ata_sleepnow(void) -{ - mutex_lock(&ata_mutex); - ata_power_down(); - mutex_unlock(&ata_mutex); -} - -void ata_close(void) -{ - ata_sleepnow(); -} - -void ata_spin(void) -{ - ata_set_active(); -} - -void ata_get_info(IF_MD(int drive,) struct storage_info *info) -{ - (*info).sector_size = SECTOR_SIZE; - (*info).num_sectors = ata_total_sectors; - (*info).vendor = "Apple"; - (*info).product = "iPod Classic"; - (*info).revision = "1.0"; -} - -long ata_last_disk_activity(void) -{ - return ata_last_activity_value; -} - -int ata_init(void) -{ - mutex_init(&ata_mutex); - semaphore_init(&ata_wakeup, 1, 0); - semaphore_init(&mmc_wakeup, 1, 0); - semaphore_init(&mmc_comp_wakeup, 1, 0); - ceata = PDAT(11) & BIT(1); - ata_powered = false; - ata_total_sectors = 0; - - /* get ata_identify_data */ - mutex_lock(&ata_mutex); - int rc = ata_power_up(); - mutex_unlock(&ata_mutex); - if (IS_ERR(rc)) return rc; - - create_thread(ata_thread, ata_stack, - sizeof(ata_stack), 0, "ATA idle monitor" - IF_PRIO(, PRIORITY_USER_INTERFACE) - IF_COP(, CPU)); - return 0; -} - -#ifdef HAVE_ATA_SMART -static int ata_smart(uint16_t* buf) -{ - if (!ata_powered) PASS_RC(ata_power_up(), 3, 0); - if (ceata) - { - memset(ceata_taskfile, 0, 16); - ceata_taskfile[0xc] = 0x4f; - ceata_taskfile[0xd] = 0xc2; - ceata_taskfile[0xe] = BIT(6); - ceata_taskfile[0xf] = 0xb0; - PASS_RC(ceata_wait_idle(), 3, 1); - if (((uint8_t*)ata_identify_data)[54] != 'A') /* Model != aAmsung */ - { - ceata_taskfile[0x9] = 0xd8; /* SMART enable operations */ - PASS_RC(ceata_write_multiple_register(0, ceata_taskfile, 16), 3, 2); - PASS_RC(ceata_check_error(), 3, 3); - } - ceata_taskfile[0x9] = 0xd0; /* SMART read data */ - PASS_RC(ceata_write_multiple_register(0, ceata_taskfile, 16), 3, 4); - PASS_RC(ceata_rw_multiple_block(false, buf, 1, CEATA_COMMAND_TIMEOUT * HZ / 1000000), 3, 5); - } - else - { - int i; - PASS_RC(ata_wait_for_not_bsy(10000000), 3, 6); - ata_write_cbr(&ATA_PIO_FED, 0xd0); - ata_write_cbr(&ATA_PIO_LMR, 0x4f); - ata_write_cbr(&ATA_PIO_LHR, 0xc2); - ata_write_cbr(&ATA_PIO_DVR, BIT(6)); - ata_write_cbr(&ATA_PIO_CSD, 0xb0); - PASS_RC(ata_wait_for_start_of_transfer(10000000), 3, 7); - for (i = 0; i < 0x100; i++) buf[i] = ata_read_cbr(&ATA_PIO_DTR); - } - ata_set_active(); - return 0; -} - -int ata_read_smart(struct ata_smart_values* smart_data) -{ - mutex_lock(&ata_mutex); - int rc = ata_smart((uint16_t*)smart_data); - mutex_unlock(&ata_mutex); - return rc; -} -#endif /* HAVE_ATA_SMART */ - -#ifdef CONFIG_STORAGE_MULTI -static int ata_num_drives(int first_drive) -{ - /* We don't care which logical drive number(s) we have been assigned */ - (void)first_drive; - - return 1; -} -#endif - -unsigned short* ata_get_identify(void) -{ - return ata_identify_data; -} - -int ata_spinup_time(void) -{ - return spinup_time; -} - -int ata_get_dma_mode(void) -{ - return dma_mode; -} - -void INT_ATA(void) -{ - uint32_t ata_irq = ATA_IRQ; - ATA_IRQ = ata_irq; - if (ata_irq & ATA_IRQ_MASK) semaphore_release(&ata_wakeup); - ATA_IRQ_MASK = 0; -} - -void INT_MMC(void) -{ - uint32_t irq = SDCI_IRQ; - if (irq & SDCI_IRQ_DAT_DONE_INT) semaphore_release(&mmc_wakeup); - if (irq & SDCI_IRQ_IOCARD_IRQ_INT) semaphore_release(&mmc_comp_wakeup); - SDCI_IRQ = irq; -} - -- cgit v1.2.3