diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index 0fad5bf5b0..c39face719 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -10517,14 +10517,17 @@
*: none
ipodvideo: "Bass Cutoff"
+ ipod6g: "Bass Cutoff"
*: none
ipodvideo: "Bass Cutoff"
+ ipod6g: "Bass Cutoff"
*: none
ipodvideo: "Bass Cutoff"
+ ipod6g: "Bass Cutoff"
@@ -10534,14 +10537,17 @@
*: none
ipodvideo: "Treble Cutoff"
+ ipod6g: "Treble Cutoff"
*: none
ipodvideo: "Treble Cutoff"
+ ipod6g: "Treble Cutoff"
*: none
ipodvideo: "Treble Cutoff"
+ ipod6g: "Treble Cutoff"
diff --git a/firmware/drivers/ata.c b/firmware/drivers/ata.c
index 77cd89f13f..08aee5dc0b 100644
--- a/firmware/drivers/ata.c
+++ b/firmware/drivers/ata.c
@@ -36,6 +36,9 @@
#include "ata-defines.h"
#include "storage.h"
+#define ATA_HAVE_BBT
+#define ATA_BBT_PAGES 2304
+
#define SECTOR_SIZE 512
#define SELECT_DEVICE1 0x10
@@ -79,6 +82,23 @@
static unsigned int ata_thread_id = 0;
#endif
+#ifdef ATA_HAVE_BBT
+uint16_t ata_bbt[ATA_BBT_PAGES][0x20];
+uint64_t virtual_sectors;
+uint32_t ata_last_offset;
+uint64_t ata_last_phys;
+int ata_rw_sectors_internal(unsigned long sector, int count, void* buffer, int write);
+int ata_rw_chunk(unsigned long sector, int count, void* buffer, int write);
+
+void ata_bbt_read_sectors(uint32_t sector, int count, void* buffer)
+{
+ int rc = ata_rw_sectors_internal(sector * 8, count * 8, buffer, 0);
+ if (rc < 0)
+ panicf("ATA: Error %d while reading BBT (sector %u, count %d)",
+ rc, (unsigned)sector, count);
+}
+#endif
+
#if defined(MAX_PHYS_SECTOR_SIZE) && MEM == 64
/* Hack - what's the deal with 5g? */
struct ata_lock
@@ -335,6 +355,109 @@ static int ata_transfer_sectors(unsigned long start,
void* inbuf,
int write)
{
+#ifdef ATA_HAVE_BBT
+char* orig = (char*)inbuf;
+ int realcount = incount;
+ incount = (incount + 7) / 8;
+ unsigned long startoffs = start & 7;
+ start /= 8;
+ if (start + incount > (unsigned)(virtual_sectors / 8)) return -9;
+ while (realcount)
+ {
+ uint32_t offset;
+ uint32_t l0idx = start >> 15;
+ uint32_t l0offs = start & 0x7fff;
+ uint32_t cnt = MIN((unsigned)incount, 0x8000 - l0offs);
+ uint32_t l0data = ata_bbt[0][l0idx << 1];
+ uint32_t base = ata_bbt[0][(l0idx << 1) | 1] << 12;
+ if (l0data < 0x8000) offset = l0data + base;
+ else
+ {
+ uint32_t l1idx = (start >> 10) & 0x1f;
+ uint32_t l1offs = start & 0x3ff;
+ cnt = MIN((unsigned)incount, 0x400 - l1offs);
+ uint32_t l1data = ata_bbt[l0data & 0x7fff][l1idx];
+ if (l1data < 0x8000) offset = l1data + base;
+ else
+ {
+ uint32_t l2idx = (start >> 5) & 0x1f;
+ uint32_t l2offs = start & 0x1f;
+ cnt = MIN((unsigned)incount, 0x20 - l2offs);
+ uint32_t l2data = ata_bbt[l1data & 0x7fff][l2idx];
+ if (l2data < 0x8000) offset = l2data + base;
+ else
+ {
+ uint32_t l3idx = start & 0x1f;
+ uint32_t l3data = ata_bbt[l2data & 0x7fff][l3idx];
+ for (cnt = 1; cnt < (unsigned)incount && l3idx + cnt < 0x20; cnt++)
+ if (ata_bbt[l2data & 0x7fff][l3idx + cnt] != l3data)
+ break;
+ offset = l3data + base;
+ }
+ }
+ }
+ uint64_t phys = start + offset;
+ if (offset != ata_last_offset && phys - ata_last_phys < 64) ata_soft_reset();
+ ata_last_offset = offset;
+ ata_last_phys = phys + cnt;
+ cnt = MIN(cnt * 8, (unsigned)realcount);
+ int rc = ata_rw_sectors_internal(phys * 8 + startoffs, cnt, inbuf, write);
+ if (rc < 0) return rc;
+ inbuf += cnt * SECTOR_SIZE;
+ start += cnt / 8;
+ incount -= cnt / 8;
+ realcount -= cnt;
+ }
+ return 0;
+}
+
+int ata_rw_sectors_internal(unsigned long start,
+ int incount,
+ void* inbuf,
+ int write)
+{
+ if (start + incount > total_sectors) return -9;
+ while (incount)
+ {
+ uint32_t cnt = MIN(lba48 ? 65536 : 256, incount);
+ int rc = -1;
+ int tries = 3;
+ while (tries-- && rc < 0)
+ {
+ rc = ata_rw_chunk(start, cnt, inbuf, write);
+ if (rc < 0) ata_soft_reset();
+ }
+ if (rc < 0)
+ {
+ void* buf = inbuf;
+ unsigned long sect;
+ for (sect = start; sect < start + cnt; sect++)
+ {
+ rc = -1;
+ tries = 3;
+ while (tries-- && rc < 0)
+ {
+ rc = ata_rw_chunk(sect, 1, buf, write);
+ if (rc < 0) ata_soft_reset();
+ }
+ if (rc < 0) break;
+ inbuf += SECTOR_SIZE;
+ }
+ }
+ if (rc < 0) return rc;
+ inbuf += SECTOR_SIZE * cnt;
+ start += cnt;
+ incount -= cnt;
+ }
+ return 0;
+}
+
+int ata_rw_chunk(unsigned long start,
+ int incount,
+ void* inbuf,
+ int write)
+{
+#endif
int ret = 0;
long timeout;
int count;
@@ -1389,6 +1512,31 @@ int ata_init(void)
mutex_lock(&ata_mtx); /* Balance unlock below */
+#ifdef ATA_HAVE_BBT
+ memset(ata_bbt, 0, sizeof(ata_bbt));
+ uint32_t* buf = (uint32_t*)(ata_bbt[ARRAYLEN(ata_bbt) - 64]);
+ ata_bbt_read_sectors(0, 1, buf);
+ if (!memcmp(buf, "emBIbbth", 8))
+ {
+ virtual_sectors = ((((uint64_t)buf[0x1fd]) << 32) | buf[0x1fc]) * 8;
+ uint32_t count = buf[0x1ff];
+ if (count > (ATA_BBT_PAGES >> 6))
+ panicf("ATA: BBT too big! (%d pages, limit: %d)",
+ (unsigned)(count << 6), ATA_BBT_PAGES);
+ uint32_t i;
+ uint32_t cnt;
+ for (i = 0; i < count; i += cnt)
+ {
+ uint32_t phys = buf[0x200 + i];
+ for (cnt = 1; cnt < count; cnt++)
+ if (buf[0x200 + i + cnt] != phys + cnt)
+ break;
+ ata_bbt_read_sectors(phys, cnt, ata_bbt[i << 6]);
+ }
+ }
+ else virtual_sectors = total_sectors;
+#endif
+
last_disk_activity = current_tick;
#ifdef ATA_DRIVER_CLOSE
ata_thread_id =
@@ -1456,7 +1604,11 @@ void ata_get_info(IF_MD2(int drive,)struct storage_info *info)
#endif
int i;
info->sector_size = SECTOR_SIZE;
+#ifdef ATA_HAVE_BBT
+ info->num_sectors = virtual_sectors;
+#else
info->num_sectors = total_sectors;
+#endif
src = (unsigned short*)&identify_info[27];
dest = (unsigned short*)vendor;
diff --git a/firmware/drivers/audio/cs42l55.c b/firmware/drivers/audio/cs42l55.c
index 79b6d283b6..5541a26ee0 100644
--- a/firmware/drivers/audio/cs42l55.c
+++ b/firmware/drivers/audio/cs42l55.c
@@ -36,6 +36,8 @@ const struct sound_settings_info audiohw_settings[] = {
[SOUND_BALANCE] = {"%", 0, 1,-100, 100, 0},
[SOUND_CHANNELS] = {"", 0, 1, 0, 5, 0},
[SOUND_STEREO_WIDTH] = {"%", 0, 5, 0, 250, 100},
+ [SOUND_BASS_CUTOFF] = {"", 0, 1, 1, 4, 2},
+ [SOUND_TREBLE_CUTOFF] = {"", 0, 1, 1, 4, 1},
};
static int bass, treble;
@@ -157,24 +159,56 @@ void audiohw_enable_lineout(bool enable)
PWRCTL2_PDN_LINA_ALWAYS | PWRCTL2_PDN_LINB_ALWAYS);
}
+static void handle_dsp_power()
+{
+ if (bass || treble)
+ {
+ cscodec_setbits(PLAYCTL, PLAYCTL_PDN_DSP, 0);
+ cscodec_setbits(BTCTL, 0, BTCTL_TCEN);
+ }
+ else
+ {
+ cscodec_setbits(BTCTL, BTCTL_TCEN, 0);
+ cscodec_setbits(PLAYCTL, 0, PLAYCTL_PDN_DSP);
+ }
+}
+
void audiohw_set_bass(int value)
{
bass = value;
- if (bass || treble) cscodec_setbits(PLAYCTL, PLAYCTL_PDN_DSP, 0);
- else cscodec_setbits(PLAYCTL, 0, PLAYCTL_PDN_DSP);
+ handle_dsp_power();
if (value >= -105 && value <= 120)
cscodec_setbits(TONECTL, TONECTL_BASS_MASK,
- (value / 15) << TONECTL_BASS_SHIFT);
+ (8 - value / 15) << TONECTL_BASS_SHIFT);
}
void audiohw_set_treble(int value)
{
treble = value;
- if (bass || treble) cscodec_setbits(PLAYCTL, PLAYCTL_PDN_DSP, 0);
- else cscodec_setbits(PLAYCTL, 0, PLAYCTL_PDN_DSP);
+ handle_dsp_power();
if (value >= -105 && value <= 120)
cscodec_setbits(TONECTL, TONECTL_TREB_MASK,
- (value / 15) << TONECTL_TREB_SHIFT);
+ (8 - value / 15) << TONECTL_TREB_SHIFT);
+}
+
+void audiohw_set_bass_cutoff(int value)
+{
+ cscodec_setbits(BTCTL, BTCTL_BASSCF_MASK,
+ (value - 1) << BTCTL_BASSCF_SHIFT);
+}
+
+void audiohw_set_treble_cutoff(int value)
+{
+ cscodec_setbits(BTCTL, BTCTL_TREBCF_MASK,
+ (value - 1) << BTCTL_TREBCF_SHIFT);
+}
+
+void audiohw_set_prescaler(int value)
+{
+ cscodec_setbits(MSTAVOL, MSTAVOL_VOLUME_MASK,
+ (-value / 5) << MSTAVOL_VOLUME_SHIFT);
+ cscodec_setbits(MSTBVOL, MSTBVOL_VOLUME_MASK,
+ (-value / 5) << MSTBVOL_VOLUME_SHIFT);
}
/* Nice shutdown of CS42L55 codec */
diff --git a/firmware/export/cs42l55.h b/firmware/export/cs42l55.h
index 8a6640f7c2..86b54ef272 100644
--- a/firmware/export/cs42l55.h
+++ b/firmware/export/cs42l55.h
@@ -26,7 +26,8 @@
#define VOLUME_MIN -580
#define VOLUME_MAX 120
-#define AUDIOHW_CAPS (BASS_CAP | TREBLE_CAP)
+#define AUDIOHW_CAPS (BASS_CAP | TREBLE_CAP | BASS_CUTOFF_CAP \
+ | TREBLE_CUTOFF_CAP | PRESCALER_CAP)
extern int tenthdb2master(int db);
@@ -269,11 +270,13 @@ extern void audiohw_enable_lineout(bool enable);
#define BTCTL 0x16
#define BTCTL_TCEN (1 << 0)
#define BTCTL_BASSCF_MASK (3 << 1)
+#define BTCTL_BASSCF_SHIFT 1
#define BTCTL_BASSCF_50 (0 << 1)
#define BTCTL_BASSCF_100 (1 << 1)
#define BTCTL_BASSCF_200 (2 << 1)
#define BTCTL_BASSCF_250 (3 << 1)
#define BTCTL_TREBCF_MASK (3 << 3)
+#define BTCTL_TREBCF_SHIFT 3
#define BTCTL_TREBCF_5000 (0 << 3)
#define BTCTL_TREBCF_7000 (1 << 3)
#define BTCTL_TREBCF_10000 (2 << 3)
diff --git a/firmware/sound.c b/firmware/sound.c
index 0eec4f8b5c..de96360198 100644
--- a/firmware/sound.c
+++ b/firmware/sound.c
@@ -308,7 +308,7 @@ void sound_set_bass(int value)
return;
#if !defined(AUDIOHW_HAVE_CLIPPING)
-#if defined(HAVE_WM8750) || defined(HAVE_WM8751)
+#if defined(HAVE_WM8750) || defined(HAVE_WM8751) || defined(HAVE_CS42L55)
current_bass = value;
#else
current_bass = value * 10;
@@ -334,7 +334,7 @@ void sound_set_treble(int value)
return;
#if !defined(AUDIOHW_HAVE_CLIPPING)
-#if defined(HAVE_WM8750) || defined(HAVE_WM8751)
+#if defined(HAVE_WM8750) || defined(HAVE_WM8751) || defined(HAVE_CS42L55)
current_treble = value;
#else
current_treble = value * 10;