summaryrefslogtreecommitdiffstats
path: root/firmware
diff options
context:
space:
mode:
Diffstat (limited to 'firmware')
-rw-r--r--firmware/SOURCES272
-rw-r--r--firmware/asm/arm/corelock.c4
-rw-r--r--firmware/asm/arm/lcd-as-memframe.S4
-rw-r--r--firmware/asm/arm/memcpy.S20
-rw-r--r--firmware/asm/arm/memmove.S20
-rw-r--r--firmware/asm/arm/memset.S26
-rw-r--r--firmware/asm/arm/memset16.S20
-rw-r--r--firmware/asm/arm/thread.c6
-rw-r--r--firmware/asm/thread.h10
-rw-r--r--firmware/backlight.c96
-rw-r--r--firmware/buflib_malloc.c263
-rw-r--r--firmware/buflib_mempool.c (renamed from firmware/buflib.c)540
-rw-r--r--firmware/chunk_alloc.c319
-rw-r--r--firmware/common/adler32.c97
-rw-r--r--firmware/common/crc32-mi4.c110
-rw-r--r--firmware/common/crc32.c169
-rw-r--r--firmware/common/diacritic.c3
-rw-r--r--firmware/common/dir.c102
-rw-r--r--firmware/common/dircache.c94
-rw-r--r--firmware/common/disk.c228
-rw-r--r--firmware/common/file.c58
-rw-r--r--firmware/common/file_internal.c120
-rw-r--r--firmware/common/fileobj_mgr.c103
-rw-r--r--firmware/common/inflate.c793
-rw-r--r--firmware/common/linked_list.c53
-rw-r--r--firmware/common/multiboot.c113
-rw-r--r--firmware/common/pathfuncs.c161
-rw-r--r--firmware/common/rb-loader.c95
-rw-r--r--firmware/common/rb_namespace.c383
-rw-r--r--firmware/common/rectangle.c141
-rw-r--r--firmware/common/strlcat.c19
-rw-r--r--firmware/common/strlcpy.c64
-rw-r--r--firmware/common/strmemccpy.c (renamed from firmware/target/arm/imx233/samsung-ypz5/debug-ypz5.c)23
-rw-r--r--firmware/common/strnatcmp.c16
-rw-r--r--firmware/common/strptokspn.c77
-rw-r--r--firmware/common/structec.c193
-rw-r--r--firmware/common/timefuncs.c51
-rw-r--r--firmware/common/ucl_decompress.c192
-rw-r--r--firmware/common/unicode.c35
-rw-r--r--firmware/common/vuprintf.c12
-rw-r--r--firmware/common/zip.c875
-rw-r--r--firmware/core_alloc.c79
-rw-r--r--firmware/drivers/ata.c135
-rw-r--r--firmware/drivers/ata_flash.c484
-rw-r--r--firmware/drivers/audio/aic3x.c8
-rw-r--r--firmware/drivers/audio/ak4376.c106
-rw-r--r--firmware/drivers/audio/eros_qn_codec.c (renamed from firmware/target/arm/pnx0101/iriver-ifp7xx/power-ifp7xx.c)72
-rw-r--r--firmware/drivers/audio/erosqlinux_codec.c40
-rw-r--r--firmware/drivers/audio/es9018.c4
-rw-r--r--firmware/drivers/audio/es9018k2m.c161
-rw-r--r--firmware/drivers/audio/es9218.c226
-rw-r--r--firmware/drivers/audio/fiiolinux_codec.c16
-rw-r--r--firmware/drivers/audio/rocker_codec.c25
-rw-r--r--firmware/drivers/audio/x1000-codec.c286
-rw-r--r--firmware/drivers/audio/xduoolinux_codec.c30
-rw-r--r--firmware/drivers/axp-pmu.c550
-rw-r--r--firmware/drivers/axp173.c419
-rw-r--r--firmware/drivers/button.c241
-rw-r--r--firmware/drivers/cw2015.c191
-rw-r--r--firmware/drivers/fat.c110
-rw-r--r--firmware/drivers/ft6x06.c129
-rw-r--r--firmware/drivers/generic_i2c.c34
-rw-r--r--firmware/drivers/isp1583.c6
-rw-r--r--firmware/drivers/lcd-16bit-common.c826
-rw-r--r--firmware/drivers/lcd-16bit-vert.c214
-rw-r--r--firmware/drivers/lcd-16bit.c213
-rw-r--r--firmware/drivers/lcd-1bit-vert.c394
-rw-r--r--firmware/drivers/lcd-24bit.c436
-rw-r--r--firmware/drivers/lcd-2bit-horz.c442
-rw-r--r--firmware/drivers/lcd-2bit-vert.c448
-rw-r--r--firmware/drivers/lcd-2bit-vi.c450
-rw-r--r--firmware/drivers/lcd-bitmap-common.c349
-rw-r--r--firmware/drivers/lcd-color-common.c181
-rw-r--r--firmware/drivers/lcd-scroll.c14
-rw-r--r--firmware/drivers/libertas/firmware/LICENCE.Marvell22
-rw-r--r--firmware/drivers/libertas/firmware/gspi8686_v9.binbin0 -> 126652 bytes
-rw-r--r--firmware/drivers/libertas/firmware/gspi8686_v9_helper.binbin0 -> 2140 bytes
-rw-r--r--firmware/drivers/libertas/if_spi.c674
-rw-r--r--firmware/drivers/libertas/if_spi.h215
-rw-r--r--firmware/drivers/libertas/if_spi_drv.h (renamed from firmware/target/arm/tcc77x/c100/backlight-target.h)26
-rw-r--r--firmware/drivers/m66591.c4
-rw-r--r--firmware/drivers/rds.c37
-rw-r--r--firmware/drivers/rtc/rtc_d2.c2
-rw-r--r--firmware/drivers/rtc/rtc_ds1339_ds3231.c2
-rw-r--r--firmware/drivers/rtc/rtc_e8564.c2
-rw-r--r--firmware/drivers/rtc/rtc_mr100.c2
-rw-r--r--firmware/drivers/rtc/rtc_pcf50605.c9
-rw-r--r--firmware/drivers/rtc/rtc_pcf50606.c3
-rw-r--r--firmware/drivers/rtc/rtc_rx5x348ab.c2
-rw-r--r--firmware/drivers/rtc/rtc_s35380a.c2
-rw-r--r--firmware/drivers/rtc/rtc_s35390a.c2
-rw-r--r--firmware/drivers/rtc/rtc_s3c2440.c2
-rw-r--r--firmware/drivers/rtc/rtc_tcc77x.c81
-rw-r--r--firmware/drivers/synaptics-mep.c5
-rw-r--r--firmware/drivers/tuner/lv24020lp.c10
-rw-r--r--firmware/drivers/tuner/si4700.c84
-rw-r--r--firmware/drivers/usb-designware.c (renamed from firmware/target/arm/usb-designware.c)565
-rw-r--r--firmware/events.c97
-rw-r--r--firmware/export/ak4376.h46
-rw-r--r--firmware/export/ata.h3
-rw-r--r--firmware/export/audio.h1
-rw-r--r--firmware/export/audiohw.h23
-rw-r--r--firmware/export/axp-pmu.h149
-rw-r--r--firmware/export/axp173.h94
-rw-r--r--firmware/export/backlight.h6
-rw-r--r--firmware/export/backtrace.h3
-rw-r--r--firmware/export/button.h2
-rw-r--r--firmware/export/config.h162
-rw-r--r--firmware/export/config/aigoerosq.h1
-rw-r--r--firmware/export/config/android.h3
-rw-r--r--firmware/export/config/cowond2.h3
-rw-r--r--firmware/export/config/erosqnative.h146
-rw-r--r--firmware/export/config/fiiom3k.h54
-rw-r--r--firmware/export/config/fiiom3klinux.h2
-rw-r--r--firmware/export/config/gigabeats.h3
-rw-r--r--firmware/export/config/hibylinux.h2
-rw-r--r--firmware/export/config/iaudio7.h164
-rw-r--r--firmware/export/config/iaudiox5.h1
-rw-r--r--firmware/export/config/ibassodx50.h6
-rw-r--r--firmware/export/config/ibassodx90.h6
-rw-r--r--firmware/export/config/ipod6g.h9
-rw-r--r--firmware/export/config/iriverifp7xx.h129
-rw-r--r--firmware/export/config/logikdax.h152
-rw-r--r--firmware/export/config/mrobe500.h7
-rw-r--r--firmware/export/config/nokian8xx.h3
-rw-r--r--firmware/export/config/nokian900.h3
-rw-r--r--firmware/export/config/pandora.h3
-rw-r--r--firmware/export/config/samsungypr0.h7
-rw-r--r--firmware/export/config/samsungypr1.h3
-rw-r--r--firmware/export/config/samsungypz5.h204
-rw-r--r--firmware/export/config/sansac100.h130
-rw-r--r--firmware/export/config/sansaclipplus.h8
-rw-r--r--firmware/export/config/sansaclipzip.h1
-rw-r--r--firmware/export/config/sansaconnect.h38
-rw-r--r--firmware/export/config/sansae200.h1
-rw-r--r--firmware/export/config/sansafuze.h1
-rw-r--r--firmware/export/config/sansafuzeplus.h1
-rw-r--r--firmware/export/config/sansafuzev2.h1
-rw-r--r--firmware/export/config/sansam200.h139
-rw-r--r--firmware/export/config/sansam200v4.h3
-rw-r--r--firmware/export/config/sdlapp.h3
-rw-r--r--firmware/export/config/shanlingq1.h136
-rw-r--r--firmware/export/config/tatungtpj1022.h151
-rw-r--r--firmware/export/config/xduoox3.h2
-rw-r--r--firmware/export/cpu.h6
-rw-r--r--firmware/export/cw2015.h57
-rw-r--r--firmware/export/disk.h3
-rw-r--r--firmware/export/dm320.h21
-rw-r--r--firmware/export/eros_qn_codec.h56
-rw-r--r--firmware/export/erosqlinux_codec.h11
-rw-r--r--firmware/export/es9018k2m.h74
-rw-r--r--firmware/export/es9218.h230
-rw-r--r--firmware/export/events.h12
-rw-r--r--firmware/export/fat.h3
-rw-r--r--firmware/export/font.h4
-rw-r--r--firmware/export/ft6x06.h (renamed from firmware/target/arm/pnx0101/iriver-ifp7xx/usb-ifp7xx.c)68
-rw-r--r--firmware/export/gdb_api.h3
-rw-r--r--firmware/export/generic_i2c.h10
-rw-r--r--firmware/export/i2c-async.h9
-rw-r--r--firmware/export/i2c-coldfire.h2
-rw-r--r--firmware/export/i2c-rk27xx.h2
-rw-r--r--firmware/export/i2c-s5l8700.h2
-rw-r--r--firmware/export/i2c-s5l8702.h2
-rw-r--r--firmware/export/i2c.h4
-rw-r--r--firmware/export/ifp_usb_serial.h30
-rw-r--r--firmware/export/jz4740.h3
-rw-r--r--firmware/export/jz4760b.h5
-rw-r--r--firmware/export/lcd.h15
-rw-r--r--firmware/export/linuxboot.h192
-rw-r--r--firmware/export/logf.h2
-rw-r--r--firmware/export/mi4-loader.h22
-rw-r--r--firmware/export/multiboot.h (renamed from firmware/target/arm/tatung/tpj1022/adc-target.h)23
-rw-r--r--firmware/export/mv.h13
-rw-r--r--firmware/export/pathfuncs.h10
-rw-r--r--firmware/export/pcm-internal.h2
-rw-r--r--firmware/export/pcm_sampr.h6
-rw-r--r--firmware/export/powermgmt.h53
-rw-r--r--firmware/export/rbpaths.h3
-rw-r--r--firmware/export/rectangle.h75
-rw-r--r--firmware/export/screendump.h14
-rw-r--r--firmware/export/si4700.h12
-rw-r--r--firmware/export/structec.h33
-rw-r--r--firmware/export/system.h13
-rw-r--r--firmware/export/tcc77x.h262
-rw-r--r--firmware/export/ucl_decompress.h32
-rw-r--r--firmware/export/usb-designware.h3
-rw-r--r--firmware/export/usb.h3
-rw-r--r--firmware/export/usb_ch9.h17
-rw-r--r--firmware/export/usb_core.h12
-rw-r--r--firmware/export/usb_drv.h11
-rw-r--r--firmware/export/wifi.h (renamed from firmware/target/arm/pnx0101/i2c-pnx0101.c)13
-rw-r--r--firmware/export/x1000-codec.h184
-rw-r--r--firmware/export/x1000.h124
-rw-r--r--firmware/export/xduoolinux_codec.h5
-rw-r--r--firmware/font.c164
-rw-r--r--firmware/general.c4
-rw-r--r--firmware/ifp_usb_serial.c1121
-rw-r--r--firmware/include/adler32.h (renamed from firmware/target/arm/tcc77x/iaudio7/adc-target.h)13
-rw-r--r--firmware/include/buflib.h591
-rw-r--r--firmware/include/buflib_malloc.h65
-rw-r--r--firmware/include/buflib_mempool.h97
-rw-r--r--firmware/include/chunk_alloc.h71
-rw-r--r--firmware/include/core_alloc.h48
-rw-r--r--firmware/include/crc32-mi4.h25
-rw-r--r--firmware/include/crc32.h1
-rw-r--r--firmware/include/dir.h6
-rw-r--r--firmware/include/dircache_redirect.h76
-rw-r--r--firmware/include/file.h3
-rw-r--r--firmware/include/file_internal.h32
-rw-r--r--firmware/include/fileobj_mgr.h5
-rw-r--r--firmware/include/filesystem-native.h3
-rw-r--r--firmware/include/fs_defines.h18
-rw-r--r--firmware/include/inflate.h70
-rw-r--r--firmware/include/linked_list.h38
-rw-r--r--firmware/include/rb-loader.h20
-rw-r--r--firmware/include/rb_namespace.h82
-rw-r--r--firmware/include/rbendian.h237
-rw-r--r--firmware/include/string-extra.h1
-rw-r--r--firmware/include/strmemccpy.h (renamed from firmware/target/arm/pnx0101/iriver-ifp7xx/adc-target.h)19
-rw-r--r--firmware/include/strptokspn_r.h (renamed from firmware/target/arm/tcc77x/c100/adc-target.h)10
-rw-r--r--firmware/include/timefuncs.h4
-rw-r--r--firmware/include/zip.h68
-rw-r--r--firmware/kernel/include/mutex.h5
-rw-r--r--firmware/kernel/include/queue.h3
-rw-r--r--firmware/kernel/include/tick.h2
-rw-r--r--firmware/kernel/pthread/corelock.c18
-rw-r--r--firmware/kernel/pthread/mutex.c21
-rw-r--r--firmware/kernel/pthread/thread.c204
-rw-r--r--firmware/kernel/thread.c9
-rw-r--r--firmware/libc/atoi.c5
-rw-r--r--firmware/libc/include/string.h4
-rw-r--r--firmware/libc/memccpy.c119
-rw-r--r--firmware/libc/strcspn.c45
-rw-r--r--firmware/libc/strtok.c63
-rw-r--r--firmware/linuxboot.c335
-rw-r--r--firmware/logf.c11
-rw-r--r--firmware/panic.c16
-rw-r--r--firmware/pcm.c4
-rw-r--r--firmware/pcm_sw_volume.c142
-rw-r--r--firmware/powermgmt.c645
-rw-r--r--firmware/rolo.c39
-rw-r--r--firmware/screendump.c4
-rw-r--r--firmware/scroll_engine.c36
-rw-r--r--firmware/system.c6
-rw-r--r--firmware/target/arm/as3525/audio-as3525.c1
-rw-r--r--firmware/target/arm/as3525/debug-as3525.c11
-rw-r--r--firmware/target/arm/as3525/fmradio-i2c-as3525.c6
-rw-r--r--firmware/target/arm/as3525/lcd-ssd1303.c3
-rw-r--r--firmware/target/arm/as3525/pcm-as3525.c12
-rw-r--r--firmware/target/arm/as3525/sansa-clipzip/lcd-clipzip.c3
-rw-r--r--firmware/target/arm/as3525/system-as3525.c2
-rw-r--r--firmware/target/arm/as3525/usb-as3525.c13
-rw-r--r--firmware/target/arm/as3525/usb-drv-as3525.c11
-rw-r--r--firmware/target/arm/ata-as-arm.S32
-rw-r--r--firmware/target/arm/ata-nand-telechips.c11
-rw-r--r--firmware/target/arm/imx233/creative-zen/lcd-zenmozaic.c4
-rw-r--r--firmware/target/arm/imx233/creative-zen/lcd-zenv.c3
-rw-r--r--firmware/target/arm/imx233/creative-zen/lcd-zenxfistyle.c3
-rw-r--r--firmware/target/arm/imx233/creative-zenxfi2/lcd-zenxfi2.c3
-rw-r--r--firmware/target/arm/imx233/creative-zenxfi3/lcd-zenxfi3.c3
-rw-r--r--firmware/target/arm/imx233/debug-imx233.c6
-rw-r--r--firmware/target/arm/imx233/pcm-imx233.c12
-rw-r--r--firmware/target/arm/imx233/samsung-ypz5/audio-target.h31
-rw-r--r--firmware/target/arm/imx233/samsung-ypz5/backlight-target.h28
-rw-r--r--firmware/target/arm/imx233/samsung-ypz5/backlight-ypz5.c149
-rw-r--r--firmware/target/arm/imx233/samsung-ypz5/button-target.h59
-rw-r--r--firmware/target/arm/imx233/samsung-ypz5/button-ypz5.c273
-rw-r--r--firmware/target/arm/imx233/samsung-ypz5/fmradio-target.h31
-rw-r--r--firmware/target/arm/imx233/samsung-ypz5/lcd-target.h25
-rw-r--r--firmware/target/arm/imx233/samsung-ypz5/lcd-ypz5.c295
-rw-r--r--firmware/target/arm/imx233/samsung-ypz5/powermgmt-target.h37
-rw-r--r--firmware/target/arm/imx233/samsung-ypz5/powermgmt-ypz5.c48
-rw-r--r--firmware/target/arm/imx233/sansa-fuzeplus/lcd-fuzeplus.c3
-rw-r--r--firmware/target/arm/imx233/sony-nwz/lcd-nwze360.c3
-rw-r--r--firmware/target/arm/imx233/sony-nwz/lcd-nwze370.c3
-rw-r--r--firmware/target/arm/imx233/touchscreen-imx233.c27
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c49
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c28
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/system-gigabeat-s.c2
-rw-r--r--firmware/target/arm/imx31/i2c-imx31.c2
-rw-r--r--firmware/target/arm/imx31/i2c-imx31.h2
-rw-r--r--firmware/target/arm/ipod/backlight-mini1g_mini2g.c7
-rw-r--r--firmware/target/arm/ipod/backlight-target.h2
-rw-r--r--firmware/target/arm/ipod/button-target.h9
-rw-r--r--firmware/target/arm/ipod/lcd-gray.c9
-rw-r--r--firmware/target/arm/ipod/video/lcd-as-video.S26
-rw-r--r--firmware/target/arm/lcd-ssd1815.c81
-rw-r--r--firmware/target/arm/olympus/mrobe-100/lcd-mr100.c6
-rw-r--r--firmware/target/arm/pcm-telechips.c32
-rw-r--r--firmware/target/arm/pnx0101/app.lds144
-rw-r--r--firmware/target/arm/pnx0101/backlight-target.h28
-rw-r--r--firmware/target/arm/pnx0101/crt0-pnx0101.S225
-rw-r--r--firmware/target/arm/pnx0101/debug-pnx0101.c5
-rw-r--r--firmware/target/arm/pnx0101/iriver-ifp7xx/button-ifp7xx.c90
-rw-r--r--firmware/target/arm/pnx0101/iriver-ifp7xx/button-target.h49
-rw-r--r--firmware/target/arm/pnx0101/iriver-ifp7xx/lcd-ifp7xx.c224
-rw-r--r--firmware/target/arm/pnx0101/iriver-ifp7xx/powermgmt-ifp7xx.c55
-rw-r--r--firmware/target/arm/pnx0101/iriver-ifp7xx/system-target.h36
-rw-r--r--firmware/target/arm/pnx0101/kernel-pnx0101.c45
-rw-r--r--firmware/target/arm/pnx0101/pcm-pnx0101.c207
-rw-r--r--firmware/target/arm/pnx0101/system-pnx0101.c317
-rw-r--r--firmware/target/arm/pnx0101/timer-pnx0101.c81
-rw-r--r--firmware/target/arm/pp/mi4-loader.c94
-rw-r--r--firmware/target/arm/pp/pcm-pp.c21
-rw-r--r--firmware/target/arm/pp/system-pp502x.c2
-rw-r--r--firmware/target/arm/pp/system-target.h6
-rw-r--r--firmware/target/arm/pp/usb-fw-pp502x.c6
-rw-r--r--firmware/target/arm/rk27xx/lcdif-rk27xx.c3
-rw-r--r--firmware/target/arm/rk27xx/pcm-rk27xx.c12
-rw-r--r--firmware/target/arm/rk27xx/usb-drv-rk27xx.c4
-rw-r--r--firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c8
-rw-r--r--firmware/target/arm/s3c2440/i2c-s3c2440.h2
-rw-r--r--firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c8
-rw-r--r--firmware/target/arm/s5l8700/debug-s5l8700.c4
-rw-r--r--firmware/target/arm/s5l8700/ipodnano2g/button-target.h9
-rw-r--r--firmware/target/arm/s5l8700/ipodnano2g/rtc-nano2g.c2
-rw-r--r--firmware/target/arm/s5l8700/pcm-s5l8700.c6
-rw-r--r--firmware/target/arm/s5l8700/yps3/lcd-yps3.c11
-rw-r--r--firmware/target/arm/s5l8702/debug-s5l8702.c4
-rw-r--r--firmware/target/arm/s5l8702/ipod6g/button-target.h9
-rw-r--r--firmware/target/arm/s5l8702/ipod6g/rtc-6g.c2
-rw-r--r--firmware/target/arm/s5l8702/ipod6g/storage_ata-6g.c47
-rw-r--r--firmware/target/arm/s5l8702/pcm-s5l8702.c7
-rw-r--r--firmware/target/arm/samsung/yh920/lcd-yh920.c3
-rw-r--r--firmware/target/arm/system-arm.h2
-rw-r--r--firmware/target/arm/tatung/app.lds2
-rw-r--r--firmware/target/arm/tatung/boot.lds2
-rw-r--r--firmware/target/arm/tatung/tpj1022/button-target.h51
-rw-r--r--firmware/target/arm/tatung/tpj1022/button-tpj1022.c49
-rw-r--r--firmware/target/arm/tatung/tpj1022/lcd-tpj1022.c85
-rw-r--r--firmware/target/arm/tatung/tpj1022/power-tpj1022.c63
-rw-r--r--firmware/target/arm/tatung/tpj1022/powermgmt-tpj1022.c62
-rw-r--r--firmware/target/arm/tcc77x/adc-tcc77x.c121
-rw-r--r--firmware/target/arm/tcc77x/app.lds99
-rw-r--r--firmware/target/arm/tcc77x/ata-nand-target.h33
-rw-r--r--firmware/target/arm/tcc77x/boot.lds63
-rw-r--r--firmware/target/arm/tcc77x/c100/button-c100.c64
-rw-r--r--firmware/target/arm/tcc77x/c100/lcd-S6B33B2.c286
-rw-r--r--firmware/target/arm/tcc77x/c100/power-c100.c43
-rw-r--r--firmware/target/arm/tcc77x/crt0.S230
-rw-r--r--firmware/target/arm/tcc77x/debug-tcc77x.c77
-rw-r--r--firmware/target/arm/tcc77x/i2c-target.h39
-rw-r--r--firmware/target/arm/tcc77x/iaudio7/ata2501.c110
-rw-r--r--firmware/target/arm/tcc77x/iaudio7/ata2501.h27
-rw-r--r--firmware/target/arm/tcc77x/iaudio7/audio-iaudio7.c94
-rw-r--r--firmware/target/arm/tcc77x/iaudio7/backlight-target.h48
-rw-r--r--firmware/target/arm/tcc77x/iaudio7/button-iaudio7.c93
-rw-r--r--firmware/target/arm/tcc77x/iaudio7/button-target.h47
-rw-r--r--firmware/target/arm/tcc77x/iaudio7/lcd-iaudio7.c260
-rw-r--r--firmware/target/arm/tcc77x/iaudio7/power-iaudio7.c149
-rw-r--r--firmware/target/arm/tcc77x/iaudio7/powermgmt-iaudio7.c84
-rw-r--r--firmware/target/arm/tcc77x/logikdax/adc-target.h28
-rw-r--r--firmware/target/arm/tcc77x/logikdax/audio-logikdax.c40
-rw-r--r--firmware/target/arm/tcc77x/logikdax/button-logikdax.c100
-rw-r--r--firmware/target/arm/tcc77x/logikdax/button-target.h45
-rw-r--r--firmware/target/arm/tcc77x/m200/adc-target.h28
-rw-r--r--firmware/target/arm/tcc77x/m200/audio-m200.c38
-rw-r--r--firmware/target/arm/tcc77x/m200/backlight-target.h44
-rw-r--r--firmware/target/arm/tcc77x/m200/button-m200.c99
-rw-r--r--firmware/target/arm/tcc77x/m200/button-target.h45
-rw-r--r--firmware/target/arm/tcc77x/m200/power-m200.c43
-rw-r--r--firmware/target/arm/tcc77x/powermgmt-tcc77x.c66
-rw-r--r--firmware/target/arm/tcc77x/system-target.h59
-rw-r--r--firmware/target/arm/tcc77x/system-tcc77x.c326
-rw-r--r--firmware/target/arm/tcc77x/timer-tcc77x.c69
-rw-r--r--firmware/target/arm/tms320dm320/app.lds60
-rw-r--r--firmware/target/arm/tms320dm320/boot.lds72
-rw-r--r--firmware/target/arm/tms320dm320/creative-zvm/ata-creativezvm.c2
-rw-r--r--firmware/target/arm/tms320dm320/creative-zvm/pcm-creativezvm.c6
-rw-r--r--firmware/target/arm/tms320dm320/crt0.S6
-rw-r--r--firmware/target/arm/tms320dm320/i2c-dm320.c8
-rw-r--r--firmware/target/arm/tms320dm320/i2c-dm320.h5
-rw-r--r--firmware/target/arm/tms320dm320/mrobe-500/lcd-mr500.c4
-rw-r--r--firmware/target/arm/tms320dm320/mrobe-500/pcm-mr500.c16
-rw-r--r--firmware/target/arm/tms320dm320/sansa-connect/avr-sansaconnect.c805
-rw-r--r--firmware/target/arm/tms320dm320/sansa-connect/avr-sansaconnect.h33
-rw-r--r--firmware/target/arm/tms320dm320/sansa-connect/backlight-sansaconnect.c3
-rw-r--r--firmware/target/arm/tms320dm320/sansa-connect/crt0-board.S64
-rw-r--r--firmware/target/arm/tms320dm320/sansa-connect/cryptomem-sansaconnect.c80
-rw-r--r--firmware/target/arm/tms320dm320/sansa-connect/cryptomem-sansaconnect.h (renamed from firmware/target/arm/tcc77x/kernel-tcc77x.c)32
-rw-r--r--firmware/target/arm/tms320dm320/sansa-connect/lcd-sansaconnect.c121
-rw-r--r--firmware/target/arm/tms320dm320/sansa-connect/pcm-sansaconnect.c16
-rw-r--r--firmware/target/arm/tms320dm320/sansa-connect/power-sansaconnect.c40
-rw-r--r--firmware/target/arm/tms320dm320/sansa-connect/powermgmt-sansaconnect.c65
-rw-r--r--firmware/target/arm/tms320dm320/sansa-connect/tnetv105_cppi.c1047
-rw-r--r--firmware/target/arm/tms320dm320/sansa-connect/tnetv105_cppi.h144
-rw-r--r--firmware/target/arm/tms320dm320/sansa-connect/tnetv105_usb_drv.c1519
-rw-r--r--firmware/target/arm/tms320dm320/sansa-connect/tnetv105_usb_drv.h335
-rw-r--r--firmware/target/arm/tms320dm320/sansa-connect/usb-sansaconnect.c67
-rw-r--r--firmware/target/arm/tms320dm320/sansa-connect/wifi-sansaconnect.c132
-rw-r--r--firmware/target/arm/tms320dm320/sdmmc-dm320.c101
-rw-r--r--firmware/target/arm/tms320dm320/system-dm320.c18
-rw-r--r--firmware/target/arm/tms320dm320/system-target.h8
-rw-r--r--firmware/target/arm/usb-drv-arc.c4
-rw-r--r--firmware/target/arm/usb-s3c6400x.c4
-rw-r--r--firmware/target/arm/usb-tcc.c4
-rw-r--r--firmware/target/arm/wmcodec-telechips.c2
-rw-r--r--firmware/target/coldfire/iaudio/m3/lcd-m3.c6
-rw-r--r--firmware/target/coldfire/iaudio/m5/lcd-m5.c6
-rw-r--r--firmware/target/coldfire/iriver/h100/lcd-h100.c6
-rw-r--r--firmware/target/coldfire/mpio/hd300/lcd-hd300.c3
-rw-r--r--firmware/target/coldfire/pcm-coldfire.c15
-rw-r--r--firmware/target/hosted/android/pcm-android.c7
-rw-r--r--firmware/target/hosted/backtrace-glibc.c2
-rw-r--r--firmware/target/hosted/filesystem-app.c14
-rw-r--r--firmware/target/hosted/filesystem-app.h1
-rw-r--r--firmware/target/hosted/filesystem-hosted.h1
-rw-r--r--firmware/target/hosted/filesystem-unix.c12
-rw-r--r--firmware/target/hosted/filesystem-win32.c7
-rw-r--r--firmware/target/hosted/ibasso/dx50/adc-target.h (renamed from firmware/target/mips/ingenic_x1000/fiiom3k/powermgmt-target.h)0
-rw-r--r--firmware/target/hosted/ibasso/dx90/adc-target.h0
-rw-r--r--firmware/target/hosted/ibasso/pcm-ibasso.c13
-rw-r--r--firmware/target/hosted/ibasso/sysfs-ibasso.c2
-rw-r--r--firmware/target/hosted/ibasso/vold-ibasso.c2
-rw-r--r--firmware/target/hosted/maemo/pcm-gstreamer.c8
-rw-r--r--firmware/target/hosted/pcm-alsa.c16
-rw-r--r--firmware/target/hosted/rolo.c16
-rw-r--r--firmware/target/hosted/rtc.c3
-rw-r--r--firmware/target/hosted/samsungypr/radio-ypr.c59
-rw-r--r--firmware/target/hosted/sdl/lcd-sdl.c6
-rw-r--r--firmware/target/hosted/sdl/pcm-sdl.c7
-rw-r--r--firmware/target/hosted/sdl/sim-ui-defines.h32
-rw-r--r--firmware/target/hosted/sdl/thread-sdl.c2
-rw-r--r--firmware/target/hosted/system-hosted.c3
-rw-r--r--firmware/target/hosted/usb-hiby.c4
-rw-r--r--firmware/target/hosted/xduoo/button-xduoo.c6
-rw-r--r--firmware/target/mips/exception-mips.S181
-rw-r--r--firmware/target/mips/ingenic_jz47xx/app.lds37
-rw-r--r--firmware/target/mips/ingenic_jz47xx/boot.lds15
-rw-r--r--firmware/target/mips/ingenic_jz47xx/crt0.S186
-rw-r--r--firmware/target/mips/ingenic_jz47xx/debug-jz4760.c8
-rw-r--r--firmware/target/mips/ingenic_jz47xx/pcm-jz4740.c41
-rw-r--r--firmware/target/mips/ingenic_jz47xx/pcm-jz4760.c41
-rw-r--r--firmware/target/mips/ingenic_jz47xx/system-jz4740.c76
-rw-r--r--firmware/target/mips/ingenic_jz47xx/system-jz4760.c48
-rw-r--r--firmware/target/mips/ingenic_jz47xx/system-target.h1
-rw-r--r--firmware/target/mips/ingenic_jz47xx/usb-jz4740.c8
-rw-r--r--firmware/target/mips/ingenic_jz47xx/usb-jz4760.c274
-rw-r--r--firmware/target/mips/ingenic_jz47xx/xduoo_x3/lcd-xduoo_x3.c6
-rw-r--r--firmware/target/mips/ingenic_x1000/aic-x1000.c134
-rw-r--r--firmware/target/mips/ingenic_x1000/aic-x1000.h130
-rw-r--r--firmware/target/mips/ingenic_x1000/app.lds94
-rw-r--r--firmware/target/mips/ingenic_x1000/boot-x1000.c285
-rw-r--r--firmware/target/mips/ingenic_x1000/boot-x1000.h81
-rw-r--r--firmware/target/mips/ingenic_x1000/boot.lds6
-rw-r--r--firmware/target/mips/ingenic_x1000/clk-x1000.c326
-rw-r--r--firmware/target/mips/ingenic_x1000/clk-x1000.h30
-rw-r--r--firmware/target/mips/ingenic_x1000/crt0.S298
-rw-r--r--firmware/target/mips/ingenic_x1000/debug-x1000.c43
-rw-r--r--firmware/target/mips/ingenic_x1000/dma-x1000.h7
-rw-r--r--firmware/target/mips/ingenic_x1000/erosqnative/adc-target.h0
-rw-r--r--firmware/target/mips/ingenic_x1000/erosqnative/audiohw-erosqnative.c182
-rw-r--r--firmware/target/mips/ingenic_x1000/erosqnative/backlight-erosqnative.c (renamed from firmware/target/arm/tatung/tpj1022/backlight-tpj1022.c)47
-rw-r--r--firmware/target/mips/ingenic_x1000/erosqnative/backlight-target.h (renamed from firmware/target/arm/tcc77x/logikdax/power-logikdax.c)31
-rw-r--r--firmware/target/mips/ingenic_x1000/erosqnative/button-erosqnative.c251
-rw-r--r--firmware/target/mips/ingenic_x1000/erosqnative/button-target.h (renamed from firmware/target/arm/tcc77x/c100/button-target.h)36
-rw-r--r--firmware/target/mips/ingenic_x1000/erosqnative/gpio-target.h75
-rw-r--r--firmware/target/mips/ingenic_x1000/erosqnative/i2c-target.h (renamed from firmware/target/arm/tatung/tpj1022/backlight-target.h)19
-rw-r--r--firmware/target/mips/ingenic_x1000/erosqnative/lcd-erosqnative.c211
-rw-r--r--firmware/target/mips/ingenic_x1000/erosqnative/power-erosqnative.c130
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/audiohw-fiiom3k.c183
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/backlight-fiiom3k.c1
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c235
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/gpio-target.h26
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/i2c-target.h4
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c195
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/lcd-fiiom3k.c25
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/nand-fiiom3k.c53
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c62
-rw-r--r--firmware/target/mips/ingenic_x1000/gpio-x1000.c120
-rw-r--r--firmware/target/mips/ingenic_x1000/gpio-x1000.h169
-rw-r--r--firmware/target/mips/ingenic_x1000/i2c-x1000.c18
-rw-r--r--firmware/target/mips/ingenic_x1000/i2c-x1000.h2
-rw-r--r--firmware/target/mips/ingenic_x1000/installer-x1000.c341
-rw-r--r--firmware/target/mips/ingenic_x1000/installer-x1000.h56
-rw-r--r--firmware/target/mips/ingenic_x1000/kernel-x1000.c22
-rw-r--r--firmware/target/mips/ingenic_x1000/lcd-x1000.c68
-rw-r--r--firmware/target/mips/ingenic_x1000/lcd-x1000.h31
-rw-r--r--firmware/target/mips/ingenic_x1000/msc-x1000.c155
-rw-r--r--firmware/target/mips/ingenic_x1000/msc-x1000.h14
-rw-r--r--firmware/target/mips/ingenic_x1000/nand-x1000-err.h18
-rw-r--r--firmware/target/mips/ingenic_x1000/nand-x1000.c770
-rw-r--r--firmware/target/mips/ingenic_x1000/nand-x1000.h360
-rw-r--r--firmware/target/mips/ingenic_x1000/pcm-x1000.c261
-rw-r--r--firmware/target/mips/ingenic_x1000/pwm-x1000.c50
-rw-r--r--firmware/target/mips/ingenic_x1000/sd-x1000.c3
-rw-r--r--firmware/target/mips/ingenic_x1000/sfc-x1000.c320
-rw-r--r--firmware/target/mips/ingenic_x1000/sfc-x1000.h152
-rw-r--r--firmware/target/mips/ingenic_x1000/shanlingq1/adc-target.h0
-rw-r--r--firmware/target/mips/ingenic_x1000/shanlingq1/audiohw-shanlingq1.c191
-rw-r--r--firmware/target/mips/ingenic_x1000/shanlingq1/backlight-shanlingq1.c (renamed from firmware/target/arm/pnx0101/iriver-ifp7xx/backlight-ifp7xx.c)40
-rw-r--r--firmware/target/mips/ingenic_x1000/shanlingq1/backlight-target.h (renamed from firmware/export/installer.h)16
-rw-r--r--firmware/target/mips/ingenic_x1000/shanlingq1/button-shanlingq1.c258
-rw-r--r--firmware/target/mips/ingenic_x1000/shanlingq1/button-target.h56
-rw-r--r--firmware/target/mips/ingenic_x1000/shanlingq1/gpio-target.h32
-rw-r--r--firmware/target/mips/ingenic_x1000/shanlingq1/i2c-target.h (renamed from firmware/target/arm/tcc77x/logikdax/backlight-target.h)32
-rw-r--r--firmware/target/mips/ingenic_x1000/shanlingq1/lcd-shanlingq1.c399
-rw-r--r--firmware/target/mips/ingenic_x1000/shanlingq1/power-shanlingq1.c163
-rw-r--r--firmware/target/mips/ingenic_x1000/spl-nand-x1000.c (renamed from firmware/target/arm/pnx0101/iriver-ifp7xx/adc-ifp7xx.c)51
-rw-r--r--firmware/target/mips/ingenic_x1000/spl-start.S97
-rw-r--r--firmware/target/mips/ingenic_x1000/spl-x1000.c371
-rw-r--r--firmware/target/mips/ingenic_x1000/spl-x1000.h (renamed from firmware/target/mips/ingenic_x1000/fiiom3k/nand-target.h)38
-rw-r--r--firmware/target/mips/ingenic_x1000/spl.lds31
-rw-r--r--firmware/target/mips/ingenic_x1000/system-target.h35
-rw-r--r--firmware/target/mips/ingenic_x1000/system-x1000.c118
-rw-r--r--firmware/target/mips/ingenic_x1000/usb-x1000.c214
-rw-r--r--firmware/target/mips/ingenic_x1000/x1000/aic.h270
-rw-r--r--firmware/target/mips/ingenic_x1000/x1000/cpm.h576
-rw-r--r--firmware/target/mips/ingenic_x1000/x1000/efuse.h173
-rw-r--r--firmware/target/mips/ingenic_x1000/x1000/ost.h10
-rw-r--r--firmware/target/mips/ingenic_x1000/x1000/pcm.h251
-rw-r--r--firmware/target/mips/ingenic_x1000/x1000/ssi.h323
-rw-r--r--firmware/target/mips/ingenic_x1000/x1000/uart.h390
-rw-r--r--firmware/target/mips/ingenic_x1000/x1000boot.make94
-rw-r--r--firmware/target/mips/mipsr2-endian.h7
-rw-r--r--firmware/target/mips/mmu-mips.h21
-rw-r--r--firmware/target/mips/system-mips.c175
-rw-r--r--firmware/target/mips/system-mips.h (renamed from firmware/target/arm/imx233/samsung-ypz5/ftl-target.h)31
-rw-r--r--firmware/usb.c82
-rw-r--r--firmware/usbstack/usb_class_driver.h7
-rw-r--r--firmware/usbstack/usb_core.c389
-rw-r--r--firmware/usbstack/usb_hid.c56
-rw-r--r--firmware/usbstack/usb_hid.h2
-rw-r--r--firmware/usbstack/usb_serial.c247
-rw-r--r--firmware/usbstack/usb_serial.h2
-rw-r--r--firmware/usbstack/usb_storage.c45
-rw-r--r--firmware/usbstack/usb_storage.h2
527 files changed, 27265 insertions, 20220 deletions
diff --git a/firmware/SOURCES b/firmware/SOURCES
index 3a42381003..f6d35fb5ea 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -4,7 +4,11 @@
ata_idle_notify.c
events.c
backlight.c
-buflib.c
+#if CONFIG_BUFLIB_BACKEND == BUFLIB_BACKEND_MEMPOOL
+buflib_mempool.c
+#elif CONFIG_BUFLIB_BACKEND == BUFLIB_BACKEND_MALLOC
+buflib_malloc.c
+#endif
core_alloc.c
general.c
powermgmt.c
@@ -33,6 +37,7 @@ logf.c
#endif /* ROCKBOX_HAS_LOGF */
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
load_code.c
+linuxboot.c
#ifdef RB_PROFILE
profile.c
#endif /* RB_PROFILE */
@@ -46,11 +51,14 @@ timer.c
debug.c
#endif /* PLATFORM_NATIVE */
panic.c
-
#if (CONFIG_PLATFORM & PLATFORM_HOSTED) && defined(BOOTFILE)
target/hosted/rolo.c
#endif
+#if defined(HAVE_BOOTDATA) || defined(HAVE_MULTIBOOT)
+common/multiboot.c
+#endif
+
#ifdef HAVE_SDL
target/hosted/sdl/button-sdl.c
target/hosted/sdl/kernel-sdl.c
@@ -201,39 +209,10 @@ target/hosted/samsungypr/ypr1/wmcodec-ypr1.c
target/hosted/maemo/maemo-thread.c
#endif
-/* Standard library */
-#if (CONFIG_PLATFORM & PLATFORM_NATIVE) || defined(__MINGW32__) || defined(__CYGWIN__)
-libc/strtok.c
-#endif /* PLATFORM_NATIVE || __MINGW32__ || __CYGWIN__ */
-#if (CONFIG_PLATFORM & PLATFORM_NATIVE) || defined(HAVE_ROCKBOX_C_LIBRARY)
-libc/atoi.c
-libc/errno.c
-#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
-/* our ctype.[ch] comes from newlib and is incompitble with most desktop's ctype */
-libc/ctype.c
-/* alsa on linux requires a more advanced sprintf, i.e. not ours */
-libc/sprintf.c
-#endif
-
-libc/memchr.c
-libc/memcmp.c
-
-libc/qsort.c
-libc/random.c
-libc/strcat.c
-libc/strchr.c
-libc/strcmp.c
-libc/strcpy.c
-
-libc/strncmp.c
-libc/strrchr.c
-libc/strstr.c
-libc/mktime.c
-libc/gmtime.c
-#endif /* CONFIG_PLATFORM || HAVE_ROCKBOX_C_LIBRARY */
-
/* Common */
#ifndef BOOTLOADER
+chunk_alloc.c
+common/strptokspn.c
common/ap_int.c
#endif
common/version.c
@@ -242,9 +221,6 @@ common/crc32.c
#ifdef MODEL_NUMBER
common/loader_strerror.c
#endif
-#ifdef MI4_FORMAT
-common/crc32-mi4.c
-#endif
#ifdef RKW_FORMAT
common/crc32-rkw.c
#endif
@@ -262,15 +238,51 @@ common/dircache.c
common/pathfuncs.c
common/fdprintf.c
common/linked_list.c
+#if (!(CONFIG_PLATFORM & PLATFORM_HOSTED))
+common/rb_namespace.c
+#endif
+common/rectangle.c
common/strcasecmp.c
common/strcasestr.c
common/strnatcmp.c
common/strlcat.c
common/strlcpy.c
-common/structec.c
+common/strmemccpy.c
common/timefuncs.c
common/unicode.c
common/vuprintf.c
+common/zip.c
+common/adler32.c
+common/inflate.c
+
+/* Standard library */
+#if (CONFIG_PLATFORM & PLATFORM_NATIVE) || defined(HAVE_ROCKBOX_C_LIBRARY)
+libc/atoi.c
+libc/errno.c
+#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
+/* our ctype.[ch] comes from newlib and is incompitble with most desktop's ctype */
+libc/ctype.c
+/* alsa on linux requires a more advanced sprintf, i.e. not ours */
+libc/sprintf.c
+#endif
+
+libc/memccpy.c
+libc/memchr.c
+libc/memcmp.c
+
+libc/qsort.c
+libc/random.c
+libc/strcat.c
+libc/strchr.c
+libc/strcmp.c
+libc/strcpy.c
+libc/strcspn.c
+libc/strncmp.c
+libc/strrchr.c
+libc/strstr.c
+libc/mktime.c
+libc/gmtime.c
+#endif /* CONFIG_PLATFORM || HAVE_ROCKBOX_C_LIBRARY */
/* Display */
scroll_engine.c
@@ -281,7 +293,7 @@ font_cache.c
font.c
hangul.c
lru.c
-#ifndef BOOTLOADER
+#ifdef HAVE_SCREENDUMP
screendump.c
#endif
#if LCD_DEPTH == 1
@@ -295,7 +307,7 @@ drivers/lcd-2bit-vert.c
drivers/lcd-2bit-vi.c
#endif /* LCD_PIXELFORMAT */
#elif LCD_DEPTH == 16
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
drivers/lcd-16bit-vert.c
#else
drivers/lcd-16bit.c
@@ -336,9 +348,6 @@ drivers/touchscreen.c
/* Storage */
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
-#if (CONFIG_STORAGE & STORAGE_NAND) && (CONFIG_NAND == NAND_IFP7XX)
-drivers/ata_flash.c
-#endif
#if (CONFIG_STORAGE & STORAGE_NAND) && (CONFIG_NAND == NAND_TCC)
target/arm/ata-nand-telechips.c
#endif
@@ -392,8 +401,6 @@ drivers/rtc/rtc_rx5x348ab.c
drivers/rtc/rtc_mr100.c
#elif (CONFIG_RTC == RTC_MC13783)
drivers/rtc/rtc_mc13783.c
-#elif (CONFIG_RTC == RTC_TCC77X)
-drivers/rtc/rtc_tcc77x.c
#elif (CONFIG_RTC == RTC_JZ4740)
drivers/rtc/rtc_jz4740.c
#elif (CONFIG_RTC == RTC_JZ4760)
@@ -510,6 +517,11 @@ drivers/audio/pcm1792.c
drivers/audio/cs4398.c
#elif defined (HAVE_ES9018)
drivers/audio/es9018.c
+#elif defined (HAVE_ES9218)
+drivers/audio/es9218.c
+#elif defined (HAVE_EROS_QN_CODEC)
+drivers/audio/eros_qn_codec.c
+drivers/audio/es9018k2m.c
#endif /* defined(HAVE_*) */
#else /* PLATFORM_HOSTED */
#if defined(SAMSUNG_YPR0) && defined(HAVE_AS3514)
@@ -544,6 +556,15 @@ target/hosted/sdl/pcm-sdl.c
#endif /* !defined(BOOTLOADER) */
+/* WiFi */
+#if !defined(BOOTLOADER)
+#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
+#if defined(HAVE_W8686_SPI)
+drivers/libertas/if_spi.c
+#endif
+#endif /* (CONFIG_PLATFORM & PLATFORM_NATIVE) */
+#endif /* !defined(BOOTLOADER) */
+
/* CPU Specific - By class then particular chip if applicable */
#if defined(CPU_COLDFIRE)
@@ -603,7 +624,7 @@ target/arm/ipod/powermgmt-ipod-pcf.c
target/arm/pp/i2c-pp.c
#elif CONFIG_I2C == I2C_PNX0101
target/arm/pnx0101/i2c-pnx0101.c
-#elif CONFIG_I2C == I2C_TCC780X || CONFIG_I2C == I2C_TCC77X
+#elif CONFIG_I2C == I2C_TCC780X
target/arm/i2c-telechips.c
#elif CONFIG_I2C == I2C_S3C2440
target/arm/s3c2440/i2c-s3c2440.c
@@ -749,8 +770,6 @@ target/arm/pnx0101/crt0-pnx0101.S
target/arm/tms320dm320/crt0.S
#elif CONFIG_CPU==S3C2440
target/arm/s3c2440/crt0.S
-#elif defined(CPU_TCC77X)
-target/arm/tcc77x/crt0.S
#elif defined(CPU_TCC780X)
target/arm/tcc780x/crt0.S
#elif CONFIG_CPU==IMX31L
@@ -768,10 +787,14 @@ target/arm/crt0.S
#endif /* defined(CPU_*) */
#elif defined(CPU_MIPS) && (CONFIG_PLATFORM & PLATFORM_NATIVE)
+target/mips/exception-mips.S
target/mips/mmu-mips.c
+target/mips/system-mips.c
#if CONFIG_CPU==JZ4732 || CONFIG_CPU==JZ4760B
target/mips/ingenic_jz47xx/crt0.S
-#endif /* CONFIG_CPU == JZ4732 || JZ4760B */
+#elif CONFIG_CPU==X1000
+target/mips/ingenic_x1000/crt0.S
+#endif
#else
@@ -841,7 +864,7 @@ target/arm/as3525/usb-drv-as3525.c
#elif CONFIG_USBOTG == USBOTG_S3C6400X
target/arm/usb-s3c6400x.c
#elif CONFIG_USBOTG == USBOTG_DESIGNWARE
-target/arm/usb-designware.c
+drivers/usb-designware.c
#elif CONFIG_USBOTG == USBOTG_ISP1583
drivers/isp1583.c
#elif CONFIG_USBOTG == USBOTG_RK27XX
@@ -897,28 +920,8 @@ target/arm/s5l8700/ipodnano2g/adc-nano2g.c
target/arm/s5l8700/adc-s5l8700.c
# endif
-#elif CONFIG_I2C == I2C_TCC77X
-target/arm/tcc77x/adc-tcc77x.c
-
#endif /* CONFIG_I2C */
-#ifdef CPU_TCC77X
-target/arm/usb-tcc.c
-target/arm/tcc77x/kernel-tcc77x.c
-target/arm/tcc77x/system-tcc77x.c
-target/arm/tcc77x/timer-tcc77x.c
-# ifndef IAUDIO7
-target/arm/tcc77x/powermgmt-tcc77x.c
-# endif /* !IAUDIO7 */
-# ifndef BOOTLOADER
-# ifndef SANSA_C100
-target/arm/wmcodec-telechips.c
-# endif /* !C100 */
-target/arm/pcm-telechips.c
-target/arm/tcc77x/debug-tcc77x.c
-# endif /* !BOOTLOADER */
-#endif /* CPU_TCC77X */
-
#if CONFIG_CPU == S5L8700 || CONFIG_CPU == S5L8701
target/arm/s5l8700/kernel-s5l8700.c
target/arm/s5l8700/system-s5l8700.c
@@ -1063,12 +1066,9 @@ target/coldfire/iaudio/audio-iaudio.c
#endif
#endif /* IAUDIO_M3 */
-#ifdef IRIVER_IFP7XX_SERIES
#ifdef STUB
-ifp_usb_serial.c
libc/sscanf.c
#endif /* STUB */
-#endif /* IRIVER_IFP7XX_SERIES */
#if defined(IRIVER_H300_SERIES) || defined(IRIVER_H100_SERIES)
target/coldfire/iriver/ata-iriver.c
@@ -1293,11 +1293,14 @@ target/arm/tms320dm320/sansa-connect/crt0-board.S
target/arm/tms320dm320/sansa-connect/lcd-sansaconnect.c
target/arm/tms320dm320/sansa-connect/adc-sansaconnect.c
target/arm/tms320dm320/sansa-connect/power-sansaconnect.c
-target/arm/tms320dm320/sansa-connect/powermgmt-sansaconnect.c
+target/arm/tms320dm320/sansa-connect/tnetv105_cppi.c
+target/arm/tms320dm320/sansa-connect/tnetv105_usb_drv.c
target/arm/tms320dm320/sansa-connect/usb-sansaconnect.c
target/arm/tms320dm320/sansa-connect/avr-sansaconnect.c
+target/arm/tms320dm320/sansa-connect/cryptomem-sansaconnect.c
target/arm/tms320dm320/sansa-connect/backlight-sansaconnect.c
target/arm/tms320dm320/sansa-connect/pcm-sansaconnect.c
+target/arm/tms320dm320/sansa-connect/wifi-sansaconnect.c
target/arm/tms320dm320/dma-dm320.c
#endif /* SANSA_CONNECT */
@@ -1315,14 +1318,6 @@ target/arm/olympus/mrobe-100/power-mr100.c
target/arm/olympus/mrobe-100/powermgmt-mr100.c
#endif /* MROBE_100 */
-#ifdef TATUNG_TPJ1022
-target/arm/tatung/tpj1022/backlight-tpj1022.c
-target/arm/tatung/tpj1022/button-tpj1022.c
-target/arm/tatung/tpj1022/lcd-tpj1022.c
-target/arm/tatung/tpj1022/power-tpj1022.c
-target/arm/tatung/tpj1022/powermgmt-tpj1022.c
-#endif /* TATUNG_TPJ1022 */
-
#ifdef IPOD_4G
target/arm/ipod/backlight-4g_color.c
target/arm/ipod/button-clickwheel.c
@@ -1373,47 +1368,6 @@ target/arm/ipod/button-mini1g.c
target/arm/ipod/button-clickwheel.c
#endif /* IPOD_MINI2G */
-#ifdef IRIVER_IFP7XX
-target/arm/pnx0101/iriver-ifp7xx/adc-ifp7xx.c
-target/arm/pnx0101/iriver-ifp7xx/backlight-ifp7xx.c
-target/arm/pnx0101/iriver-ifp7xx/button-ifp7xx.c
-target/arm/pnx0101/iriver-ifp7xx/lcd-ifp7xx.c
-target/arm/pnx0101/iriver-ifp7xx/power-ifp7xx.c
-target/arm/pnx0101/iriver-ifp7xx/powermgmt-ifp7xx.c
-target/arm/pnx0101/iriver-ifp7xx/usb-ifp7xx.c
-#ifndef BOOTLOADER
-target/arm/pnx0101/pcm-pnx0101.c
-#endif /* BOOTLOADER */
-#endif /* IRIVER_IFP7XX */
-
-#ifdef LOGIK_DAX
-drivers/nand_id.c
-target/arm/tcc77x/logikdax/button-logikdax.c
-target/arm/tcc77x/logikdax/power-logikdax.c
-#ifndef BOOTLOADER
-target/arm/tcc77x/logikdax/audio-logikdax.c
-#endif /* BOOTLOADER */
-#endif /* LOGIK_DAX */
-
-#ifdef SANSA_M200
-drivers/nand_id.c
-target/arm/tcc77x/m200/button-m200.c
-target/arm/tcc77x/m200/power-m200.c
-#ifndef BOOTLOADER
-target/arm/tcc77x/m200/audio-m200.c
-#endif /* BOOTLOADER */
-#endif /* SANSA_M200 */
-
-#ifdef SANSA_C100
-drivers/nand_id.c
-target/arm/tcc77x/c100/lcd-S6B33B2.c
-target/arm/tcc77x/c100/button-c100.c
-target/arm/tcc77x/c100/power-c100.c
-#ifndef BOOTLOADER
-target/arm/tcc77x/c100/audio-c100.c
-#endif /* BOOTLOADER */
-#endif /* SANSA_C100 */
-
#ifdef SANSA_CLIPPLUS
target/arm/as3525/sansa-clipplus/lcd-clip-plus.c
target/arm/as3525/sansa-clipplus/button-clip.c
@@ -1505,17 +1459,6 @@ target/arm/imx233/sansa-fuzeplus/debug-fuzeplus.c
target/arm/imx233/sansa-fuzeplus/powermgmt-fuzeplus.c
#endif
-#ifdef SAMSUNG_YPZ5
-target/arm/imx233/samsung-ypz5/backlight-ypz5.c
-target/arm/imx233/samsung-ypz5/lcd-ypz5.c
-target/arm/imx233/samsung-ypz5/button-ypz5.c
-target/arm/imx233/samsung-ypz5/debug-ypz5.c
-target/arm/imx233/samsung-ypz5/powermgmt-ypz5.c
-#ifndef BOOTLOADER
-target/arm/imx233/fmradio-imx233.c
-#endif
-#endif
-
#ifdef SANSA_CLIPZIP
target/arm/as3525/sansa-clipzip/lcd-clipzip.c
target/arm/as3525/sansa-clipzip/button-clipzip.c
@@ -1525,19 +1468,6 @@ target/arm/as3525/sansa-clipzip/powermgmt-clipzip.c
#endif /* !BOOTLOADER */
#endif /* SANSA_CLIPZIP */
-#ifdef IAUDIO_7
-drivers/nand_id.c
-drivers/pcf50606.c
-target/arm/tcc77x/iaudio7/lcd-iaudio7.c
-target/arm/tcc77x/iaudio7/power-iaudio7.c
-target/arm/tcc77x/iaudio7/powermgmt-iaudio7.c
-target/arm/tcc77x/iaudio7/button-iaudio7.c
-target/arm/tcc77x/iaudio7/ata2501.c
-#ifndef BOOTLOADER
-target/arm/tcc77x/iaudio7/audio-iaudio7.c
-#endif /* BOOTLOADER */
-#endif /* IAUDIO_7 */
-
#ifdef COWON_D2
drivers/lcd-memframe.c
drivers/nand_id.c
@@ -1726,8 +1656,8 @@ drivers/nand_id.c
#endif /* CONFIG_CPU == JZ4760B */
#if CONFIG_CPU == X1000
-target/mips/ingenic_x1000/crt0.S
target/mips/ingenic_x1000/aic-x1000.c
+target/mips/ingenic_x1000/boot-x1000.c
target/mips/ingenic_x1000/clk-x1000.c
target/mips/ingenic_x1000/debug-x1000.c
target/mips/ingenic_x1000/dma-x1000.c
@@ -1741,12 +1671,25 @@ target/mips/ingenic_x1000/pwm-x1000.c
target/mips/ingenic_x1000/sfc-x1000.c
target/mips/ingenic_x1000/system-x1000.c
target/mips/ingenic_x1000/timer-x1000.c
+#ifndef USB_NONE
+target/mips/ingenic_x1000/usb-x1000.c
+#endif
#if (CONFIG_STORAGE & (STORAGE_SD|STORAGE_MMC|STORAGE_ATA))
target/mips/ingenic_x1000/msc-x1000.c
#endif
#if (CONFIG_STORAGE & STORAGE_SD)
target/mips/ingenic_x1000/sd-x1000.c
#endif
+#ifdef BOOTLOADER
+target/mips/ingenic_x1000/installer-x1000.c
+target/mips/ingenic_x1000/spl-start.S
+target/mips/ingenic_x1000/spl-x1000.c
+common/ucl_decompress.c
+#endif
+#if (defined(HAVE_X1000_ICODEC_PLAY) || defined(HAVE_X1000_ICODEC_REC)) \
+ && !defined(BOOTLOADER)
+drivers/audio/x1000-codec.c
+#endif
#endif /* CONFIG_CPU == X1000 */
#if defined(ONDA_VX747) || defined(ONDA_VX747P) || defined(ONDA_VX777)
@@ -1776,12 +1719,29 @@ target/mips/ingenic_jz47xx/xduoo_x3/sadc-xduoo_x3.c
target/mips/ingenic_x1000/fiiom3k/audiohw-fiiom3k.c
target/mips/ingenic_x1000/fiiom3k/backlight-fiiom3k.c
target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c
-target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c
target/mips/ingenic_x1000/fiiom3k/lcd-fiiom3k.c
-target/mips/ingenic_x1000/fiiom3k/nand-fiiom3k.c
target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c
+target/mips/ingenic_x1000/spl-nand-x1000.c
#endif /* FIIO_M3K */
+#if defined(SHANLING_Q1)
+target/mips/ingenic_x1000/shanlingq1/audiohw-shanlingq1.c
+target/mips/ingenic_x1000/shanlingq1/backlight-shanlingq1.c
+target/mips/ingenic_x1000/shanlingq1/button-shanlingq1.c
+target/mips/ingenic_x1000/shanlingq1/lcd-shanlingq1.c
+target/mips/ingenic_x1000/shanlingq1/power-shanlingq1.c
+target/mips/ingenic_x1000/spl-nand-x1000.c
+#endif /* SHANLING_Q1 */
+
+#if defined(EROS_QN)
+target/mips/ingenic_x1000/erosqnative/audiohw-erosqnative.c
+target/mips/ingenic_x1000/erosqnative/backlight-erosqnative.c
+target/mips/ingenic_x1000/erosqnative/button-erosqnative.c
+target/mips/ingenic_x1000/erosqnative/lcd-erosqnative.c
+target/mips/ingenic_x1000/erosqnative/power-erosqnative.c
+target/mips/ingenic_x1000/spl-nand-x1000.c
+#endif /* EROS_QN */
+
#if defined(LYRE_PROTO1)
target/arm/at91sam/lyre_proto1/adc-lyre_proto1.c
target/arm/at91sam/lyre_proto1/backlight-lyre_proto1.c
@@ -2003,12 +1963,20 @@ target/hosted/sdl/filesystem-sdl.c
drivers/touchpad.c
#endif
+/* Hardware drivers */
+#ifndef SIMULATOR
#ifdef HAVE_I2C_ASYNC
drivers/i2c-async.c
#endif
-
-#ifdef HAVE_AXP173
-drivers/axp173.c
+#ifdef HAVE_AXP_PMU
+drivers/axp-pmu.c
+#endif
+#ifdef HAVE_FT6x06
+drivers/ft6x06.c
+#endif
+#ifdef HAVE_CW2015
+drivers/cw2015.c
+#endif
#endif
/* firmware/kernel section */
diff --git a/firmware/asm/arm/corelock.c b/firmware/asm/arm/corelock.c
index b36a40b45b..a60299436f 100644
--- a/firmware/asm/arm/corelock.c
+++ b/firmware/asm/arm/corelock.c
@@ -61,6 +61,7 @@ int corelock_try_lock(struct corelock *cl)
/* Relies on the fact that core IDs are complementary bitmasks (0x55,0xaa) */
asm volatile (
+ BEGIN_ARM_ASM_SYNTAX_UNIFIED
"mov r1, %[id] \n" /* r1 = PROCESSOR_ID */
"ldrb r1, [r1] \n"
"strb r1, [%[cl], r1, lsr #7] \n" /* cl->myl[core] = core */
@@ -71,8 +72,9 @@ int corelock_try_lock(struct corelock *cl)
"bne 1f \n" /* yes? lock acquired */
"ldrb %[rv], [%[cl], #2] \n" /* || cl->turn == core? */
"ands %[rv], %[rv], r1 \n"
- "streqb %[rv], [%[cl], r1, lsr #7] \n" /* if not, cl->myl[core] = 0 */
+ "strbeq %[rv], [%[cl], r1, lsr #7] \n" /* if not, cl->myl[core] = 0 */
"1: \n" /* Done */
+ END_ARM_ASM_SYNTAX_UNIFIED
: [rv] "=r"(rval)
: [id] "i" (&PROCESSOR_ID), [cl] "r" (cl)
: "r1","r2","cc"
diff --git a/firmware/asm/arm/lcd-as-memframe.S b/firmware/asm/arm/lcd-as-memframe.S
index 52ab0447c2..d42b2a920d 100644
--- a/firmware/asm/arm/lcd-as-memframe.S
+++ b/firmware/asm/arm/lcd-as-memframe.S
@@ -91,9 +91,9 @@ lcd_copy_buffer_rect: @
stmia r0!, { r6-r12, r14 } @
bgt 30b @ octword loop @
40: @ finish line @
- ldreqh r6, [r1], #2 @ finish last halfword if eq ...
+ ldrheq r6, [r1], #2 @ finish last halfword if eq ...
add r1, r1, r4, lsl #1 @
- streqh r6, [r0], #2 @ ...
+ strheq r6, [r0], #2 @ ...
add r0, r0, r4, lsl #1 @
subs r3, r3, #1 @ next line
bgt 10b @ copy line @
diff --git a/firmware/asm/arm/memcpy.S b/firmware/asm/arm/memcpy.S
index 83d43293e6..86fc6b7930 100644
--- a/firmware/asm/arm/memcpy.S
+++ b/firmware/asm/arm/memcpy.S
@@ -99,22 +99,22 @@ memcpy:
7: ldmfd sp!, {r5 - r8}
8: movs r2, r2, lsl #31
- ldrneb r3, [r1], #1
- ldrcsb r4, [r1], #1
- ldrcsb ip, [r1]
- strneb r3, [r0], #1
- strcsb r4, [r0], #1
- strcsb ip, [r0]
+ ldrbne r3, [r1], #1
+ ldrbcs r4, [r1], #1
+ ldrbcs ip, [r1]
+ strbne r3, [r0], #1
+ strbcs r4, [r0], #1
+ strbcs ip, [r0]
ldmpc regs="r0, r4"
9: rsb ip, ip, #4
cmp ip, #2
- ldrgtb r3, [r1], #1
- ldrgeb r4, [r1], #1
+ ldrbgt r3, [r1], #1
+ ldrbge r4, [r1], #1
ldrb lr, [r1], #1
- strgtb r3, [r0], #1
- strgeb r4, [r0], #1
+ strbgt r3, [r0], #1
+ strbge r4, [r0], #1
subs r2, r2, ip
strb lr, [r0], #1
blt 8b
diff --git a/firmware/asm/arm/memmove.S b/firmware/asm/arm/memmove.S
index d8cab048be..e5c9b42928 100644
--- a/firmware/asm/arm/memmove.S
+++ b/firmware/asm/arm/memmove.S
@@ -106,20 +106,20 @@ memmove:
7: ldmfd sp!, {r5 - r8}
8: movs r2, r2, lsl #31
- ldrneb r3, [r1, #-1]!
- ldrcsb r4, [r1, #-1]!
- ldrcsb ip, [r1, #-1]
- strneb r3, [r0, #-1]!
- strcsb r4, [r0, #-1]!
- strcsb ip, [r0, #-1]
+ ldrbne r3, [r1, #-1]!
+ ldrbcs r4, [r1, #-1]!
+ ldrbcs ip, [r1, #-1]
+ strbne r3, [r0, #-1]!
+ strbcs r4, [r0, #-1]!
+ strbcs ip, [r0, #-1]
ldmpc regs="r0, r4"
9: cmp ip, #2
- ldrgtb r3, [r1, #-1]!
- ldrgeb r4, [r1, #-1]!
+ ldrbgt r3, [r1, #-1]!
+ ldrbge r4, [r1, #-1]!
ldrb lr, [r1, #-1]!
- strgtb r3, [r0, #-1]!
- strgeb r4, [r0, #-1]!
+ strbgt r3, [r0, #-1]!
+ strbge r4, [r0, #-1]!
subs r2, r2, ip
strb lr, [r0, #-1]!
blt 8b
diff --git a/firmware/asm/arm/memset.S b/firmware/asm/arm/memset.S
index 64cd95cc9e..d727f2a5ec 100644
--- a/firmware/asm/arm/memset.S
+++ b/firmware/asm/arm/memset.S
@@ -34,8 +34,8 @@
1: cmp r2, #4 @ 1 do we have enough
blt 5f @ 1 bytes to align with?
cmp r3, #2 @ 1
- strgtb r1, [r0, #-1]! @ 1
- strgeb r1, [r0, #-1]! @ 1
+ strbgt r1, [r0, #-1]! @ 1
+ strbge r1, [r0, #-1]! @ 1
strb r1, [r0, #-1]! @ 1
sub r2, r2, r3 @ 1 r2 = r2 - r3
b 2f
@@ -65,24 +65,24 @@ memset:
mov lr, r1
3: subs r2, r2, #64
- stmgedb r0!, {r1, r3, ip, lr} @ 64 bytes at a time.
- stmgedb r0!, {r1, r3, ip, lr}
- stmgedb r0!, {r1, r3, ip, lr}
- stmgedb r0!, {r1, r3, ip, lr}
+ stmdbge r0!, {r1, r3, ip, lr} @ 64 bytes at a time.
+ stmdbge r0!, {r1, r3, ip, lr}
+ stmdbge r0!, {r1, r3, ip, lr}
+ stmdbge r0!, {r1, r3, ip, lr}
bgt 3b
ldrpc cond=eq @ Now <64 bytes to go.
/*
* No need to correct the count; we're only testing bits from now on
*/
tst r2, #32
- stmnedb r0!, {r1, r3, ip, lr}
- stmnedb r0!, {r1, r3, ip, lr}
+ stmdbne r0!, {r1, r3, ip, lr}
+ stmdbne r0!, {r1, r3, ip, lr}
tst r2, #16
- stmnedb r0!, {r1, r3, ip, lr}
+ stmdbne r0!, {r1, r3, ip, lr}
ldr lr, [sp], #4
5: tst r2, #8
- stmnedb r0!, {r1, r3}
+ stmdbne r0!, {r1, r3}
tst r2, #4
strne r1, [r0, #-4]!
/*
@@ -90,10 +90,10 @@ memset:
* may have an unaligned pointer as well.
*/
6: tst r2, #2
- strneb r1, [r0, #-1]!
- strneb r1, [r0, #-1]!
+ strbne r1, [r0, #-1]!
+ strbne r1, [r0, #-1]!
tst r2, #1
- strneb r1, [r0, #-1]!
+ strbne r1, [r0, #-1]!
bx lr
.end:
.size memset,.end-memset
diff --git a/firmware/asm/arm/memset16.S b/firmware/asm/arm/memset16.S
index 5c787b1bed..226eac39e1 100644
--- a/firmware/asm/arm/memset16.S
+++ b/firmware/asm/arm/memset16.S
@@ -35,7 +35,7 @@
memset16:
tst r0, #2 @ unaligned?
cmpne r2, #0
- strneh r1, [r0], #2 @ store one halfword to align
+ strhne r1, [r0], #2 @ store one halfword to align
subne r2, r2, #1
/*
@@ -54,29 +54,29 @@ memset16:
mov lr, r1
2: subs r2, r2, #32
- stmgeia r0!, {r1, r3, ip, lr} @ 64 bytes at a time.
- stmgeia r0!, {r1, r3, ip, lr}
- stmgeia r0!, {r1, r3, ip, lr}
- stmgeia r0!, {r1, r3, ip, lr}
+ stmiage r0!, {r1, r3, ip, lr} @ 64 bytes at a time.
+ stmiage r0!, {r1, r3, ip, lr}
+ stmiage r0!, {r1, r3, ip, lr}
+ stmiage r0!, {r1, r3, ip, lr}
bgt 2b
ldrpc cond=eq @ Now <64 bytes to go.
/*
* No need to correct the count; we're only testing bits from now on
*/
tst r2, #16
- stmneia r0!, {r1, r3, ip, lr}
- stmneia r0!, {r1, r3, ip, lr}
+ stmiane r0!, {r1, r3, ip, lr}
+ stmiane r0!, {r1, r3, ip, lr}
tst r2, #8
- stmneia r0!, {r1, r3, ip, lr}
+ stmiane r0!, {r1, r3, ip, lr}
ldr lr, [sp], #4
4: tst r2, #4
- stmneia r0!, {r1, r3}
+ stmiane r0!, {r1, r3}
tst r2, #2
strne r1, [r0], #4
tst r2, #1
- strneh r1, [r0], #2
+ strhne r1, [r0], #2
bx lr
.end:
.size memset16,.end-memset16
diff --git a/firmware/asm/arm/thread.c b/firmware/asm/arm/thread.c
index cf685526e3..30df56e0d9 100644
--- a/firmware/asm/arm/thread.c
+++ b/firmware/asm/arm/thread.c
@@ -73,18 +73,20 @@ static inline void store_context(void* addr)
static inline void load_context(const void* addr)
{
asm volatile(
+ BEGIN_ARM_ASM_SYNTAX_UNIFIED
"ldr r0, [%0, #40] \n" /* Load start pointer */
"cmp r0, #0 \n" /* Check for NULL */
/* If not already running, jump to start */
#if ARM_ARCH == 4 && defined(USE_THUMB)
- "ldmneia %0, { r0, r12 } \n"
+ "ldmiane %0, { r0, r12 } \n"
"bxne r12 \n"
#else
- "ldmneia %0, { r0, pc } \n"
+ "ldmiane %0, { r0, pc } \n"
#endif
"ldmia %0, { r4-r11, sp, lr } \n" /* Load regs r4 to r14 from context */
+ END_ARM_ASM_SYNTAX_UNIFIED
: : "r" (addr) : "r0" /* only! */
);
}
diff --git a/firmware/asm/thread.h b/firmware/asm/thread.h
index 82edc81deb..5372be73ab 100644
--- a/firmware/asm/thread.h
+++ b/firmware/asm/thread.h
@@ -38,6 +38,16 @@ struct regs
#include <errno.h>
#ifdef HAVE_SIGALTSTACK_THREADS
#include <signal.h>
+ #ifdef _DYNAMIC_STACK_SIZE_SOURCE
+ /* glibc 2.34 made MINSIGSTKSZ non-constant. This is a problem for sim
+ * builds. Hosted targets are using ancient glibc where MINSIGSTKSZ is
+ * still a compile time constant. On platforms where this is a problem
+ * (mainly x86-64 and ARM64) the signal stack size can be big, so let's
+ * give a decent amount of space and hope for the best...
+ * FIXME: this isn't a great solution. */
+ #undef MINSIGSTKSZ
+ #define MINSIGSTKSZ 16384
+ #endif
/* MINSIGSTKSZ for the OS to deliver the signal + 0x3000 for us */
#define DEFAULT_STACK_SIZE (MINSIGSTKSZ+0x3000) /* Bytes */
#elif defined(HAVE_WIN32_FIBER_THREADS)
diff --git a/firmware/backlight.c b/firmware/backlight.c
index e8a71af12c..cc773e0a3b 100644
--- a/firmware/backlight.c
+++ b/firmware/backlight.c
@@ -21,6 +21,10 @@
*
****************************************************************************/
#include "config.h"
+#if !defined(BOOTLOADER)
+#include "settings.h"
+#include "action.h"
+#endif
#include <stdlib.h>
#include "cpu.h"
#include "kernel.h"
@@ -117,10 +121,8 @@ static int backlight_timeout_normal = 5*HZ;
#if CONFIG_CHARGING
static int backlight_timeout_plugged = 5*HZ;
#endif
-#ifdef HAS_BUTTON_HOLD
static int backlight_on_button_hold = 0;
-#endif
-static void backlight_timeout_handler(void);
+static void backlight_handle_timeout(void);
#ifdef HAVE_BUTTON_LIGHT
static int buttonlight_timer;
@@ -548,6 +550,17 @@ static void remote_backlight_update_state(void)
}
#endif /* HAVE_REMOTE_LCD */
+static void backlight_queue_wait(struct queue_event *ev)
+{
+#if (CONFIG_BACKLIGHT_FADING == BACKLIGHT_FADING_SW_SETTING) \
+ || (CONFIG_BACKLIGHT_FADING == BACKLIGHT_FADING_SW_HW_REG)
+ if (backlight_fading_state != NOT_FADING)
+ queue_wait_w_tmo(&backlight_queue, ev, FADE_DELAY);
+ else
+#endif
+ queue_wait_w_tmo(&backlight_queue, ev, BACKLIGHT_THREAD_TIMEOUT);
+}
+
void backlight_thread(void)
{
struct queue_event ev;
@@ -555,13 +568,7 @@ void backlight_thread(void)
while(1)
{
-#if (CONFIG_BACKLIGHT_FADING == BACKLIGHT_FADING_SW_SETTING) \
- || (CONFIG_BACKLIGHT_FADING == BACKLIGHT_FADING_SW_HW_REG)
- if (backlight_fading_state != NOT_FADING)
- queue_wait_w_tmo(&backlight_queue, &ev, FADE_DELAY);
- else
-#endif
- queue_wait_w_tmo(&backlight_queue, &ev, BACKLIGHT_THREAD_TIMEOUT);
+ backlight_queue_wait(&ev);
switch(ev.id)
{ /* These events must always be processed */
#ifdef _BACKLIGHT_FADE_BOOST
@@ -665,9 +672,14 @@ void backlight_thread(void)
#endif /* HAVE_BUTTONLIGHT_BRIGHTNESS */
#endif /* HAVE_BUTTON_LIGHT */
+ case SYS_REBOOT:
case SYS_POWEROFF: /* Lock backlight on poweroff so it doesn't */
locked = true; /* go off before power is actually cut. */
- /* fall through */
+#if !defined(BOOTLOADER)
+ if (!global_settings.show_shutdown_message)
+ break;
+#endif
+ /* else fall through */
#if CONFIG_CHARGING
case SYS_CHARGER_CONNECTED:
case SYS_CHARGER_DISCONNECTED:
@@ -678,24 +690,7 @@ void backlight_thread(void)
#endif
break;
case SYS_TIMEOUT:
-#if (CONFIG_BACKLIGHT_FADING == BACKLIGHT_FADING_SW_SETTING) \
- || (CONFIG_BACKLIGHT_FADING == BACKLIGHT_FADING_SW_HW_REG)
- if (backlight_fading_state != NOT_FADING)
- {
- if ((_backlight_fade_step(backlight_fading_state)))
- { /* finished fading */
-#ifdef HAVE_LCD_SLEEP
- if (backlight_fading_state == FADING_DOWN)
- { /* start sleep countdown */
- backlight_lcd_sleep_countdown(true);
- }
-#endif
- backlight_fading_state = NOT_FADING;
- }
- }
- else
-#endif /* CONFIG_BACKLIGHT_FADING */
- backlight_timeout_handler();
+ backlight_handle_timeout();
break;
}
} /* end while */
@@ -755,6 +750,28 @@ static void backlight_timeout_handler(void)
}
}
+static void backlight_handle_timeout(void)
+{
+#if (CONFIG_BACKLIGHT_FADING == BACKLIGHT_FADING_SW_SETTING) \
+ || (CONFIG_BACKLIGHT_FADING == BACKLIGHT_FADING_SW_HW_REG)
+ if (backlight_fading_state != NOT_FADING)
+ {
+ if ((_backlight_fade_step(backlight_fading_state)))
+ { /* finished fading */
+#ifdef HAVE_LCD_SLEEP
+ if (backlight_fading_state == FADING_DOWN)
+ { /* start sleep countdown */
+ backlight_lcd_sleep_countdown(true);
+ }
+#endif
+ backlight_fading_state = NOT_FADING;
+ }
+ }
+ else
+#endif /* CONFIG_BACKLIGHT_FADING */
+ backlight_timeout_handler();
+}
+
void backlight_init(void)
{
queue_init(&backlight_queue, true);
@@ -828,18 +845,18 @@ bool is_backlight_on(bool ignore_always_off)
/* return value in ticks; 0 means always on, <0 means always off */
int backlight_get_current_timeout(void)
{
-#ifdef HAS_BUTTON_HOLD
if ((backlight_on_button_hold != 0)
-#ifdef HAVE_REMOTE_LCD_AS_MAIN
+#if (defined(HAVE_REMOTE_LCD_AS_MAIN) && defined(HAS_REMOTE_BUTTON_HOLD))
&& remote_button_hold()
-#else
+#elif defined(HAS_BUTTON_HOLD)
&& button_hold()
+#else
+ && is_keys_locked()
#endif
)
return (backlight_on_button_hold == 2) ? 0 : -1;
/* always on or always off */
else
-#endif
#if CONFIG_CHARGING
if (power_input_present())
return backlight_timeout_plugged;
@@ -875,6 +892,7 @@ void backlight_hold_changed(bool hold_button)
queue_post(&backlight_queue, BACKLIGHT_ON, 0);
}
}
+#endif /* HAS_BUTTON_HOLD */
void backlight_set_on_button_hold(int index)
{
@@ -885,7 +903,6 @@ void backlight_set_on_button_hold(int index)
backlight_on_button_hold = index;
queue_post(&backlight_queue, BACKLIGHT_TMO_CHANGED, 0);
}
-#endif /* HAS_BUTTON_HOLD */
#ifdef HAVE_LCD_SLEEP_SETTING
void lcd_set_sleep_after_backlight_off(int timeout_seconds)
@@ -1047,3 +1064,14 @@ void buttonlight_set_brightness(int val) { (void)val; }
#endif /* HAVE_BUTTON_LIGHT */
#endif /* defined(HAVE_BACKLIGHT) && defined(BACKLIGHT_FULL_INIT) */
+
+#ifndef HAVE_BUTTON_LIGHT /* Dummy Functions */
+void buttonlight_on(void) {}
+void buttonlight_on_ignore(bool value, int timeout){(void)value;(void)timeout;}
+void buttonlight_off(void) {}
+void buttonlight_set_timeout(int value) {(void)value;}
+#endif /* ndef HAVE_BUTTON_LIGHT */
+
+#ifndef HAVE_BUTTONLIGHT_BRIGHTNESS /* Dummy Functions */
+void buttonlight_set_brightness(int val) { (void)val; }
+#endif /* ndef HAVE_BUTTONLIGHT_BRIGHTNESS */
diff --git a/firmware/buflib_malloc.c b/firmware/buflib_malloc.c
new file mode 100644
index 0000000000..0cd292f1e0
--- /dev/null
+++ b/firmware/buflib_malloc.c
@@ -0,0 +1,263 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2023 Aidan MacDonald
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+/*
+ * Malloc backed buflib. This is intended for debugging rather than for
+ * serious use - the buffer passed to the context is wasted, and memory
+ * is acquired from malloc() instead. The main point is to make ASAN more
+ * effective by isolating buflib allocations from each other.
+ *
+ * Currently this is a bare-minimum implementation, it doesn't even run
+ * buflib callbacks since it never moves anything. It could later be
+ * extended with stress-testing options, for example by randomly moving
+ * allocations around.
+ */
+
+#include "buflib.h"
+#include "panic.h"
+#include <stdlib.h>
+
+static struct buflib_malloc_handle *get_free_handle(struct buflib_context *ctx)
+{
+ struct buflib_malloc_handle *h;
+ for (size_t i = 0; i < ctx->num_allocs; ++i)
+ {
+ h = &ctx->allocs[i];
+ if (h->size == 0)
+ return h;
+ }
+
+ ctx->num_allocs++;
+ ctx->allocs = realloc(ctx->allocs, ctx->num_allocs * sizeof(*ctx->allocs));
+ if (!ctx->allocs)
+ panicf("buflib %p handle OOM", ctx);
+
+ h = &ctx->allocs[ctx->num_allocs - 1];
+ h->size = 0;
+ return h;
+}
+
+static int get_handle_num(struct buflib_context *ctx,
+ struct buflib_malloc_handle *handle)
+{
+ return (handle - ctx->allocs) + 1;
+}
+
+static struct buflib_malloc_handle *get_handle(struct buflib_context *ctx,
+ int handle)
+{
+ return &ctx->allocs[handle - 1];
+}
+
+struct buflib_callbacks buflib_ops_locked = {
+ .move_callback = NULL,
+ .shrink_callback = NULL,
+ .sync_callback = NULL,
+};
+
+void buflib_init(struct buflib_context *ctx, void *buf, size_t size)
+{
+ ctx->allocs = NULL;
+ ctx->num_allocs = 0;
+ ctx->buf = buf;
+ ctx->bufsize = size;
+}
+
+size_t buflib_available(struct buflib_context *ctx)
+{
+ return ctx->bufsize;
+}
+
+size_t buflib_allocatable(struct buflib_context *ctx)
+{
+ return ctx->bufsize;
+}
+
+bool buflib_context_relocate(struct buflib_context *ctx, void *buf)
+{
+ ctx->buf = buf;
+ return true;
+}
+
+int buflib_alloc(struct buflib_context *ctx, size_t size)
+{
+ return buflib_alloc_ex(ctx, size, NULL);
+}
+
+int buflib_alloc_ex(struct buflib_context *ctx, size_t size,
+ struct buflib_callbacks *ops)
+{
+ struct buflib_malloc_handle *handle = get_free_handle(ctx);
+
+ handle->data = malloc(size);
+ handle->user = handle->data;
+ handle->size = size;
+ handle->pin_count = 0;
+ handle->ops = ops;
+
+ if (!handle->data)
+ panicf("buflib %p data OOM", ctx);
+
+ return get_handle_num(ctx, handle);
+}
+
+int buflib_alloc_maximum(struct buflib_context* ctx,
+ size_t *size, struct buflib_callbacks *ops)
+{
+ *size = ctx->bufsize;
+
+ return buflib_alloc_ex(ctx, *size, ops);
+}
+
+bool buflib_shrink(struct buflib_context *ctx, int handle,
+ void *newstart, size_t new_size)
+{
+ struct buflib_malloc_handle *h = get_handle(ctx, handle);
+ if (newstart < h->user || new_size > h->size - (newstart - h->user))
+ return false;
+
+ /* XXX: this might be allowed, but what would be the point... */
+ if (new_size == 0)
+ {
+ panicf("weird shrink");
+ return false;
+ }
+
+ /* due to buflib semantics we must not realloc */
+ h->user = newstart;
+ h->size = new_size;
+ return true;
+}
+
+void buflib_pin(struct buflib_context *ctx, int handle)
+{
+ struct buflib_malloc_handle *h = get_handle(ctx, handle);
+
+ h->pin_count++;
+}
+
+void buflib_unpin(struct buflib_context *ctx, int handle)
+{
+ struct buflib_malloc_handle *h = get_handle(ctx, handle);
+
+ h->pin_count--;
+}
+
+unsigned buflib_pin_count(struct buflib_context *ctx, int handle)
+{
+ struct buflib_malloc_handle *h = get_handle(ctx, handle);
+
+ return h->pin_count;
+}
+
+void _buflib_malloc_put_data_pinned(struct buflib_context *ctx, void *data)
+{
+ for (size_t i = 0; i < ctx->num_allocs; ++i)
+ {
+ if (ctx->allocs[i].user == data)
+ {
+ ctx->allocs[i].pin_count--;
+ break;
+ }
+ }
+}
+
+int buflib_free(struct buflib_context *ctx, int handle)
+{
+ if (handle <= 0)
+ return 0;
+
+ struct buflib_malloc_handle *h = get_handle(ctx, handle);
+
+ free(h->data);
+ h->size = 0;
+
+ return 0;
+}
+
+#ifdef BUFLIB_DEBUG_GET_DATA
+void *buflib_get_data(struct buflib_context *ctx, int handle)
+{
+ /* kind of silly since it's better for ASAN to catch this but... */
+ if (handle <= 0 || handle > ctx->num_allocs)
+ panicf("buflib %p: invalid handle %d", ctx, handle);
+
+ struct buflib_malloc_handle *h = get_handle(ctx, handle);
+ if (h->user == NULL)
+ panicf("buflib %p: handle %d use after free", ctx, handle);
+
+ return h->user;
+}
+#endif
+
+void *buflib_buffer_out(struct buflib_context *ctx, size_t *size)
+{
+ if (*size == 0)
+ *size = ctx->bufsize;
+
+ void *ret = ctx->buf;
+
+ ctx->buf += *size;
+ return ret;
+}
+
+void buflib_buffer_in(struct buflib_context *ctx, int size)
+{
+ ctx->buf -= size;
+}
+
+#ifdef BUFLIB_DEBUG_PRINT
+int buflib_get_num_blocks(struct buflib_context *ctx)
+{
+ return ctx->num_allocs;
+}
+
+bool buflib_print_block_at(struct buflib_context *ctx, int block_num,
+ char *buf, size_t bufsize)
+{
+ if (block_num >= ctx->num_allocs)
+ {
+ if (bufsize > 0)
+ *buf = '\0';
+ return false;
+ }
+
+ struct buflib_malloc_handle *handle = &ctx->allocs[block_num];
+ if (handle->data)
+ {
+ snprintf(buf, bufsize, "%03d addr:%8p length:%zu",
+ block_num, handle->data, handle->size);
+ }
+ else
+ {
+ snprintf(buf, bufsize, "%03d (unallocated)", block_num);
+ }
+
+ return true;
+}
+#endif
+
+#ifdef BUFLIB_DEBUG_CHECK_VALID
+void buflib_check_valid(struct buflib_context *ctx)
+{
+ (void)ctx;
+}
+#endif
diff --git a/firmware/buflib.c b/firmware/buflib_mempool.c
index 0e90e7fe72..9d1c055bb9 100644
--- a/firmware/buflib.c
+++ b/firmware/buflib_mempool.c
@@ -30,13 +30,14 @@
#include <stdio.h> /* for snprintf() */
#include <stddef.h> /* for ptrdiff_t */
#include "buflib.h"
-#include "string-extra.h" /* strlcpy() */
+#include "string-extra.h" /* strmemccpy() */
#include "debug.h"
#include "panic.h"
-#include "crc32.h"
#include "system.h" /* for ALIGN_*() */
-/* The main goal of this design is fast fetching of the pointer for a handle.
+/* FIXME: This comment is pretty out of date now and wrong in some details.
+ *
+ * The main goal of this design is fast fetching of the pointer for a handle.
* For that reason, the handles are stored in a table at the end of the buffer
* with a fixed address, so that returning the pointer for a handle is a simple
* table lookup. To reduce the frequency with which allocated blocks will need
@@ -65,7 +66,7 @@
* H - handle table entry pointer
* C - pointer to struct buflib_callbacks
* c - variable sized string identifier
- * L2 - second length marker for string identifier
+ * L2 - length of the metadata
* crc - crc32 protecting buflib metadata integrity
* X - actual payload
* Y - unallocated space
@@ -97,11 +98,47 @@
#define BPANICF panicf
-#define IS_MOVABLE(a) (!a[2].ops || a[2].ops->move_callback)
+/* Available paranoia checks */
+#define PARANOIA_CHECK_LENGTH (1 << 0)
+#define PARANOIA_CHECK_BLOCK_HANDLE (1 << 1)
+#define PARANOIA_CHECK_PINNING (1 << 2)
+/* Bitmask of enabled paranoia checks */
+#define BUFLIB_PARANOIA \
+ (PARANOIA_CHECK_LENGTH | \
+ PARANOIA_CHECK_BLOCK_HANDLE | PARANOIA_CHECK_PINNING)
+
+struct buflib_callbacks buflib_ops_locked = {
+ .move_callback = NULL,
+ .shrink_callback = NULL,
+ .sync_callback = NULL,
+};
+
+#define IS_MOVABLE(a) \
+ (!a[BUFLIB_IDX_OPS].ops || a[BUFLIB_IDX_OPS].ops->move_callback)
+
static union buflib_data* find_first_free(struct buflib_context *ctx);
static union buflib_data* find_block_before(struct buflib_context *ctx,
union buflib_data* block,
bool is_free);
+
+/* Check the length of a block to ensure it does not go beyond the end
+ * of the allocated area. The block can be either allocated or free.
+ *
+ * This verifies that it is safe to iterate to the next block in a loop.
+ */
+static void check_block_length(struct buflib_context *ctx,
+ union buflib_data *block);
+
+/* Check a block's handle pointer to ensure it is within the handle
+ * table, and that the user pointer is pointing within the block.
+ *
+ * This verifies that it is safe to dereference the entry and ensures
+ * that the pointer in the handle table points within the block, as
+ * determined by the length field at the start of the block.
+ */
+static void check_block_handle(struct buflib_context *ctx,
+ union buflib_data *block);
+
/* Initialize buffer manager */
void
buflib_init(struct buflib_context *ctx, void *buf, size_t size)
@@ -138,7 +175,7 @@ bool buflib_context_relocate(struct buflib_context *ctx, void *buf)
/* cannot continue if the buffer is not aligned, since we would need
* to reduce the size of the buffer for aligning */
- if ((uintptr_t)buf & 0x3)
+ if (!IS_ALIGNED((uintptr_t)buf, sizeof(union buflib_data)))
return false;
/* relocate the handle table entries */
@@ -190,9 +227,22 @@ union buflib_data* handle_alloc(struct buflib_context *ctx)
if (handle >= ctx->alloc_end)
ctx->last_handle--;
else
+ {
+ /* We know the table is full, so update first_free_handle */
+ ctx->first_free_handle = ctx->last_handle - 1;
return NULL;
+ }
}
+
+ /* We know there are no free handles between the old first_free_handle
+ * and the found handle, therefore we can update first_free_handle */
+ ctx->first_free_handle = handle - 1;
+
+ /* We need to set the table entry to a non-NULL value to ensure that
+ * compactions triggered by an allocation do not compact the handle
+ * table and delete this handle. */
handle->val = -1;
+
return handle;
}
@@ -216,33 +266,35 @@ void handle_free(struct buflib_context *ctx, union buflib_data *handle)
static inline
union buflib_data* handle_to_block(struct buflib_context* ctx, int handle)
{
- union buflib_data *data = ALIGN_DOWN(buflib_get_data(ctx, handle), sizeof (*data));
- /* this is a valid case, e.g. during buflib_alloc_ex() when the handle
- * has already been allocated but not the data */
- if (!data)
+ void *ptr = buflib_get_data(ctx, handle);
+
+ /* this is a valid case for shrinking if handle
+ * was freed by the shrink callback */
+ if (!ptr)
return NULL;
- volatile size_t len = data[-2].val;
- return data - (len + 4);
+
+ return _buflib_get_block_header(ptr);
}
/* Shrink the handle table, returning true if its size was reduced, false if
* not
*/
-static inline
-bool
-handle_table_shrink(struct buflib_context *ctx)
+static inline bool handle_table_shrink(struct buflib_context *ctx)
{
- bool rv;
union buflib_data *handle;
- for (handle = ctx->last_handle; !(handle->alloc); handle++);
+ union buflib_data *old_last = ctx->last_handle;
+
+ for (handle = ctx->last_handle; handle != ctx->handle_table; ++handle)
+ if (handle->alloc)
+ break;
+
if (handle > ctx->first_free_handle)
ctx->first_free_handle = handle - 1;
- rv = handle != ctx->last_handle;
+
ctx->last_handle = handle;
- return rv;
+ return handle != old_last;
}
-
/* If shift is non-zero, it represents the number of places to move
* blocks in memory. Calculate the new address for this block,
* update its entry in the handle table, and then move its contents.
@@ -254,32 +306,21 @@ static bool
move_block(struct buflib_context* ctx, union buflib_data* block, int shift)
{
char* new_start;
+ union buflib_data *new_block;
- if (block < ctx->buf_start || block > ctx->alloc_end)
- buflib_panic(ctx, "buflib data corrupted %p", block);
-
- union buflib_data *new_block, *tmp = block[1].handle, *crc_slot;
- struct buflib_callbacks *ops = block[2].ops;
- crc_slot = (union buflib_data*)tmp->alloc - 1;
- if (crc_slot < ctx->buf_start || crc_slot > ctx->alloc_end)
- buflib_panic(ctx, "buflib metadata corrupted %p", crc_slot);
+ check_block_handle(ctx, block);
+ union buflib_data *h_entry = block[BUFLIB_IDX_HANDLE].handle;
- const int metadata_size = (crc_slot - block)*sizeof(union buflib_data);
- uint32_t crc = crc_32((void *)block, metadata_size, 0xffffffff);
-
- /* check for metadata validity */
- if (crc != crc_slot->crc)
- buflib_panic(ctx, "buflib metadata corrupted, crc: 0x%08x, expected: 0x%08x",
- (unsigned int)crc, (unsigned int)crc_slot->crc);
-
- if (!IS_MOVABLE(block))
+ if (!IS_MOVABLE(block) || block[BUFLIB_IDX_PIN].pincount > 0)
return false;
- int handle = ctx->handle_table - tmp;
- BDEBUGF("%s(): moving \"%s\"(id=%d) by %d(%d)\n", __func__, block[3].name,
+ int handle = ctx->handle_table - h_entry;
+ BDEBUGF("%s(): moving id=%d by %d(%d)\n", __func__,
handle, shift, shift*(int)sizeof(union buflib_data));
new_block = block + shift;
- new_start = tmp->alloc + shift*sizeof(union buflib_data);
+ new_start = h_entry->alloc + shift*sizeof(union buflib_data);
+
+ struct buflib_callbacks *ops = block[BUFLIB_IDX_OPS].ops;
/* If move must be synchronized with use, user should have specified a
callback that handles this */
@@ -287,10 +328,10 @@ move_block(struct buflib_context* ctx, union buflib_data* block, int shift)
ops->sync_callback(handle, true);
bool retval = false;
- if (!ops || ops->move_callback(handle, tmp->alloc, new_start)
+ if (!ops || ops->move_callback(handle, h_entry->alloc, new_start)
!= BUFLIB_CB_CANNOT_MOVE)
{
- tmp->alloc = new_start; /* update handle table */
+ h_entry->alloc = new_start; /* update handle table */
memmove(new_block, block, block->val * sizeof(union buflib_data));
retval = true;
}
@@ -327,6 +368,8 @@ buflib_compact(struct buflib_context *ctx)
* For simplicity only 1 hole at a time is considered */
for(block = find_first_free(ctx); block < ctx->alloc_end; block += len)
{
+ check_block_length(ctx, block);
+
bool movable = true; /* cache result to avoid 2nd call to move_block */
len = block->val;
/* This block is free, add its length to the shift value */
@@ -403,41 +446,52 @@ buflib_compact_and_shrink(struct buflib_context *ctx, unsigned shrink_hints)
this < ctx->alloc_end;
before = this, this += abs(this->val))
{
- if (this->val > 0 && this[2].ops
- && this[2].ops->shrink_callback)
+ check_block_length(ctx, this);
+ if (this->val < 0)
+ continue;
+
+ struct buflib_callbacks *ops = this[BUFLIB_IDX_OPS].ops;
+ if (!ops || !ops->shrink_callback)
+ continue;
+
+ check_block_handle(ctx, this);
+ union buflib_data* h_entry = this[BUFLIB_IDX_HANDLE].handle;
+ int handle = ctx->handle_table - h_entry;
+
+ unsigned pos_hints = shrink_hints & BUFLIB_SHRINK_POS_MASK;
+ /* adjust what we ask for if there's free space in the front
+ * this isn't too unlikely assuming this block is
+ * shrinkable but not movable */
+ if (pos_hints == BUFLIB_SHRINK_POS_FRONT &&
+ before != this && before->val < 0)
{
- int ret;
- int handle = ctx->handle_table - this[1].handle;
- char* data = this[1].handle->alloc;
- bool last = (this+this->val) == ctx->alloc_end;
- unsigned pos_hints = shrink_hints & BUFLIB_SHRINK_POS_MASK;
- /* adjust what we ask for if there's free space in the front
- * this isn't too unlikely assuming this block is
- * shrinkable but not movable */
- if (pos_hints == BUFLIB_SHRINK_POS_FRONT
- && before != this && before->val < 0)
- {
- size_t free_space = (-before->val) * sizeof(union buflib_data);
- size_t wanted = shrink_hints & BUFLIB_SHRINK_SIZE_MASK;
- if (wanted < free_space) /* no shrink needed? */
- continue;
- wanted -= free_space;
- shrink_hints = pos_hints | wanted;
- }
- ret = this[2].ops->shrink_callback(handle, shrink_hints,
- data, (char*)(this+this->val)-data);
- result |= (ret == BUFLIB_CB_OK);
- /* 'this' might have changed in the callback (if it shrinked
- * from the top or even freed the handle), get it again */
- this = handle_to_block(ctx, handle);
- /* The handle was possibly be freed in the callback,
- * re-run the loop with the handle before */
- if (!this)
- this = before;
- /* could also change with shrinking from back */
- else if (last)
- ctx->alloc_end = this + this->val;
+ size_t free_space = (-before->val) * sizeof(union buflib_data);
+ size_t wanted = shrink_hints & BUFLIB_SHRINK_SIZE_MASK;
+ if (wanted < free_space) /* no shrink needed? */
+ continue;
+ wanted -= free_space;
+ shrink_hints = pos_hints | wanted;
}
+
+ char* data = h_entry->alloc;
+ char* data_end = (char*)(this + this->val);
+ bool last = (data_end == (char*)ctx->alloc_end);
+
+ int ret = ops->shrink_callback(handle, shrink_hints,
+ data, data_end - data);
+ result |= (ret == BUFLIB_CB_OK);
+
+ /* 'this' might have changed in the callback (if it shrinked
+ * from the top or even freed the handle), get it again */
+ this = handle_to_block(ctx, handle);
+
+ /* The handle was possibly be freed in the callback,
+ * re-run the loop with the handle before */
+ if (!this)
+ this = before;
+ /* could also change with shrinking from back */
+ else if (last)
+ ctx->alloc_end = this + this->val;
}
/* shrinking was successful at least once, try compaction again */
if (result)
@@ -500,13 +554,12 @@ buflib_buffer_in(struct buflib_context *ctx, int size)
int
buflib_alloc(struct buflib_context *ctx, size_t size)
{
- return buflib_alloc_ex(ctx, size, NULL, NULL);
+ return buflib_alloc_ex(ctx, size, NULL);
}
/* Allocate a buffer of size bytes, returning a handle for it.
*
- * The additional name parameter gives the allocation a human-readable name,
- * the ops parameter points to caller-implemented callbacks for moving and
+ * The ops parameter points to caller-implemented callbacks for moving and
* shrinking.
*
* If you pass NULL for "ops", buffers are movable by default.
@@ -515,20 +568,16 @@ buflib_alloc(struct buflib_context *ctx, size_t size)
*/
int
-buflib_alloc_ex(struct buflib_context *ctx, size_t size, const char *name,
+buflib_alloc_ex(struct buflib_context *ctx, size_t size,
struct buflib_callbacks *ops)
{
union buflib_data *handle, *block;
- size_t name_len = name ? B_ALIGN_UP(strlen(name)+1) : 0;
bool last;
/* This really is assigned a value before use */
int block_len;
- size += name_len;
size = (size + sizeof(union buflib_data) - 1) /
sizeof(union buflib_data)
- /* add 5 objects for alloc len, pointer to handle table entry and
- * name length, the ops pointer and crc */
- + 5;
+ + BUFLIB_NUM_FIELDS;
handle_alloc:
handle = handle_alloc(ctx);
if (!handle)
@@ -538,7 +587,7 @@ handle_alloc:
*/
union buflib_data* last_block = find_block_before(ctx,
ctx->alloc_end, false);
- struct buflib_callbacks* ops = last_block[2].ops;
+ struct buflib_callbacks* ops = last_block[BUFLIB_IDX_OPS].ops;
unsigned hints = 0;
if (!ops || !ops->shrink_callback)
{ /* the last one isn't shrinkable
@@ -562,7 +611,7 @@ buffer_alloc:
/* need to re-evaluate last before the loop because the last allocation
* possibly made room in its front to fit this, so last would be wrong */
last = false;
- for (block = find_first_free(ctx);;block += block_len)
+ for (block = find_first_free(ctx);; block += block_len)
{
/* If the last used block extends all the way to the handle table, the
* block "after" it doesn't have a header. Because of this, it's easier
@@ -578,6 +627,8 @@ buffer_alloc:
block = NULL;
break;
}
+
+ check_block_length(ctx, block);
block_len = block->val;
/* blocks with positive length are already allocated. */
if(block_len > 0)
@@ -598,7 +649,6 @@ buffer_alloc:
{
goto buffer_alloc;
} else {
- handle->val=1;
handle_free(ctx, handle);
return -2;
}
@@ -607,23 +657,15 @@ buffer_alloc:
/* Set up the allocated block, by marking the size allocated, and storing
* a pointer to the handle.
*/
- union buflib_data *name_len_slot, *crc_slot;
- block->val = size;
- block[1].handle = handle;
- block[2].ops = ops;
- if (name_len > 0)
- strcpy(block[3].name, name);
- name_len_slot = (union buflib_data*)B_ALIGN_UP(block[3].name + name_len);
- name_len_slot->val = 1 + name_len/sizeof(union buflib_data);
- crc_slot = (union buflib_data*)(name_len_slot + 1);
- crc_slot->crc = crc_32((void *)block,
- (crc_slot - block)*sizeof(union buflib_data),
- 0xffffffff);
- handle->alloc = (char*)(crc_slot + 1);
-
- BDEBUGF("buflib_alloc_ex: size=%d handle=%p clb=%p crc=0x%0x name=\"%s\"\n",
- (unsigned int)size, (void *)handle, (void *)ops,
- (unsigned int)crc_slot->crc, name ? block[3].name:"");
+ block[BUFLIB_IDX_LEN].val = size;
+ block[BUFLIB_IDX_HANDLE].handle = handle;
+ block[BUFLIB_IDX_OPS].ops = ops;
+ block[BUFLIB_IDX_PIN].pincount = 0;
+
+ handle->alloc = (char*)&block[BUFLIB_NUM_FIELDS];
+
+ BDEBUGF("buflib_alloc_ex: size=%d handle=%p clb=%p\n",
+ (unsigned int)size, (void *)handle, (void *)ops);
block += size;
/* alloc_end must be kept current if we're taking the last block. */
@@ -639,13 +681,14 @@ buffer_alloc:
static union buflib_data*
find_first_free(struct buflib_context *ctx)
{
- union buflib_data* ret = ctx->buf_start;
- while(ret < ctx->alloc_end)
+ union buflib_data *ret;
+ for(ret = ctx->buf_start; ret < ctx->alloc_end; ret += ret->val)
{
+ check_block_length(ctx, ret);
if (ret->val < 0)
break;
- ret += ret->val;
}
+
/* ret is now either a free block or the same as alloc_end, both is fine */
return ret;
}
@@ -658,29 +701,31 @@ find_block_before(struct buflib_context *ctx, union buflib_data* block,
union buflib_data *ret = ctx->buf_start,
*next_block = ret;
+ /* no previous block */
+ if (next_block == block)
+ return NULL;
+
/* find the block that's before the current one */
- while (next_block < block)
+ while (next_block != block)
{
+ check_block_length(ctx, ret);
ret = next_block;
next_block += abs(ret->val);
}
- /* If next_block == block, the above loop didn't go anywhere. If it did,
- * and the block before this one is empty, that is the wanted one
- */
- if (next_block == block && ret < block)
- {
- if (is_free && ret->val >= 0) /* NULL if found block isn't free */
- return NULL;
- return ret;
- }
- return NULL;
+ /* don't return it if the found block isn't free */
+ if (is_free && ret->val >= 0)
+ return NULL;
+
+ return ret;
}
/* Free the buffer associated with handle_num. */
int
buflib_free(struct buflib_context *ctx, int handle_num)
{
+ if (handle_num <= 0) /* invalid or already free */
+ return handle_num;
union buflib_data *handle = ctx->handle_table - handle_num,
*freed_block = handle_to_block(ctx, handle_num),
*block, *next_block;
@@ -694,13 +739,13 @@ buflib_free(struct buflib_context *ctx, int handle_num)
}
else
{
- /* Otherwise, set block to the newly-freed block, and mark it free, before
- * continuing on, since the code below expects block to point to a free
- * block which may have free space after it.
- */
+ /* Otherwise, set block to the newly-freed block, and mark it free,
+ * before continuing on, since the code below expects block to point
+ * to a free block which may have free space after it. */
block = freed_block;
block->val = -block->val;
}
+
next_block = block - block->val;
/* Check if we are merging with the free space at alloc_end. */
if (next_block == ctx->alloc_end)
@@ -723,11 +768,10 @@ static size_t
free_space_at_end(struct buflib_context* ctx)
{
/* subtract 5 elements for
- * val, handle, name_len, ops and the handle table entry*/
- ptrdiff_t diff = (ctx->last_handle - ctx->alloc_end - 5);
+ * val, handle, meta_len, ops and the handle table entry*/
+ ptrdiff_t diff = (ctx->last_handle - ctx->alloc_end - BUFLIB_NUM_FIELDS);
diff -= 16; /* space for future handles */
diff *= sizeof(union buflib_data); /* make it bytes */
- diff -= 16; /* reserve 16 for the name */
if (diff > 0)
return diff;
@@ -739,24 +783,31 @@ free_space_at_end(struct buflib_context* ctx)
size_t
buflib_allocatable(struct buflib_context* ctx)
{
- union buflib_data *this;
size_t free_space = 0, max_free_space = 0;
+ intptr_t block_len;
/* make sure buffer is as contiguous as possible */
if (!ctx->compact)
buflib_compact(ctx);
/* now look if there's free in holes */
- for(this = find_first_free(ctx); this < ctx->alloc_end; this += abs(this->val))
+ for(union buflib_data *block = find_first_free(ctx);
+ block < ctx->alloc_end;
+ block += block_len)
{
- if (this->val < 0)
+ check_block_length(ctx, block);
+ block_len = block->val;
+
+ if (block_len < 0)
{
- free_space += -this->val;
+ block_len = -block_len;
+ free_space += block_len;
continue;
}
+
/* an unmovable section resets the count as free space
* can't be contigous */
- if (!IS_MOVABLE(this))
+ if (!IS_MOVABLE(block))
{
if (max_free_space < free_space)
max_free_space = free_space;
@@ -779,17 +830,16 @@ buflib_allocatable(struct buflib_context* ctx)
size_t
buflib_available(struct buflib_context* ctx)
{
- union buflib_data *this;
size_t free_space = 0;
- /* now look if there's free in holes */
- for(this = find_first_free(ctx); this < ctx->alloc_end; this += abs(this->val))
+ /* add up all holes */
+ for(union buflib_data *block = find_first_free(ctx);
+ block < ctx->alloc_end;
+ block += abs(block->val))
{
- if (this->val < 0)
- {
- free_space += -this->val;
- continue;
- }
+ check_block_length(ctx, block);
+ if (block->val < 0)
+ free_space += -block->val;
}
free_space *= sizeof(union buflib_data); /* make it bytes */
@@ -807,11 +857,8 @@ buflib_available(struct buflib_context* ctx)
* serviced anyway).
*/
int
-buflib_alloc_maximum(struct buflib_context* ctx, const char* name, size_t *size, struct buflib_callbacks *ops)
+buflib_alloc_maximum(struct buflib_context* ctx, size_t *size, struct buflib_callbacks *ops)
{
- /* limit name to 16 since that's what buflib_available() accounts for it */
- char buf[16];
-
/* ignore ctx->compact because it's true if all movable blocks are contiguous
* even if the buffer has holes due to unmovable allocations */
unsigned hints;
@@ -827,9 +874,7 @@ buflib_alloc_maximum(struct buflib_context* ctx, const char* name, size_t *size,
if (*size <= 0) /* OOM */
return -1;
- strlcpy(buf, name, sizeof(buf));
-
- return buflib_alloc_ex(ctx, *size, buf, ops);
+ return buflib_alloc_ex(ctx, *size, ops);
}
/* Shrink the allocation indicated by the handle according to new_start and
@@ -839,10 +884,8 @@ buflib_alloc_maximum(struct buflib_context* ctx, const char* name, size_t *size,
bool
buflib_shrink(struct buflib_context* ctx, int handle, void* new_start, size_t new_size)
{
- union buflib_data *crc_slot;
- int size_for_crc32;
char* oldstart = buflib_get_data(ctx, handle);
- char* newstart = new_start;
+ char* newstart = new_start != NULL ? new_start : oldstart;
char* newend = newstart + new_size;
/* newstart must be higher and new_size not "negative" */
@@ -864,9 +907,10 @@ buflib_shrink(struct buflib_context* ctx, int handle, void* new_start, size_t ne
metadata_size.val = aligned_oldstart - block;
/* update val and the handle table entry */
new_block = aligned_newstart - metadata_size.val;
- block[0].val = new_next_block - new_block;
+ block[BUFLIB_IDX_LEN].val = new_next_block - new_block;
- block[1].handle->alloc = newstart;
+ check_block_handle(ctx, block);
+ block[BUFLIB_IDX_HANDLE].handle->alloc = newstart;
if (block != new_block)
{
/* move metadata over, i.e. pointer to handle table entry and name
@@ -886,11 +930,6 @@ buflib_shrink(struct buflib_context* ctx, int handle, void* new_start, size_t ne
block = new_block;
}
- /* update crc of the metadata */
- crc_slot = (union buflib_data*)new_block[1].handle->alloc - 1;
- size_for_crc32 = (crc_slot - new_block)*sizeof(union buflib_data);
- crc_slot->crc = crc_32((void *)new_block, size_for_crc32, 0xffffffff);
-
/* Now deal with size changes that create free blocks after the allocation */
if (old_next_block != new_next_block)
{
@@ -910,17 +949,40 @@ buflib_shrink(struct buflib_context* ctx, int handle, void* new_start, size_t ne
return true;
}
-const char* buflib_get_name(struct buflib_context *ctx, int handle)
+void buflib_pin(struct buflib_context *ctx, int handle)
{
- union buflib_data *data = ALIGN_DOWN(buflib_get_data(ctx, handle), sizeof (*data));
- size_t len = data[-2].val;
- if (len <= 1)
- return NULL;
- return data[-len-1].name;
+ if ((BUFLIB_PARANOIA & PARANOIA_CHECK_PINNING) && handle <= 0)
+ buflib_panic(ctx, "invalid handle pin: %d", handle);
+
+ union buflib_data *data = handle_to_block(ctx, handle);
+ data[BUFLIB_IDX_PIN].pincount++;
}
-#ifdef DEBUG
+void buflib_unpin(struct buflib_context *ctx, int handle)
+{
+ if ((BUFLIB_PARANOIA & PARANOIA_CHECK_PINNING) && handle <= 0)
+ buflib_panic(ctx, "invalid handle unpin: %d", handle);
+ union buflib_data *data = handle_to_block(ctx, handle);
+ if (BUFLIB_PARANOIA & PARANOIA_CHECK_PINNING)
+ {
+ if (data[BUFLIB_IDX_PIN].pincount == 0)
+ buflib_panic(ctx, "handle pin underflow: %d", handle);
+ }
+
+ data[BUFLIB_IDX_PIN].pincount--;
+}
+
+unsigned buflib_pin_count(struct buflib_context *ctx, int handle)
+{
+ if ((BUFLIB_PARANOIA & PARANOIA_CHECK_PINNING) && handle <= 0)
+ buflib_panic(ctx, "invalid handle: %d", handle);
+
+ union buflib_data *data = handle_to_block(ctx, handle);
+ return data[BUFLIB_IDX_PIN].pincount;
+}
+
+#ifdef BUFLIB_DEBUG_GET_DATA
void *buflib_get_data(struct buflib_context *ctx, int handle)
{
if (handle <= 0)
@@ -928,105 +990,115 @@ void *buflib_get_data(struct buflib_context *ctx, int handle)
return (void*)(ctx->handle_table[-handle].alloc);
}
+#endif
+#ifdef BUFLIB_DEBUG_CHECK_VALID
void buflib_check_valid(struct buflib_context *ctx)
{
- union buflib_data *crc_slot;
- int metadata_size;
- uint32_t crc;
-
- for(union buflib_data* this = ctx->buf_start;
- this < ctx->alloc_end;
- this += abs(this->val))
+ for(union buflib_data *block = ctx->buf_start;
+ block < ctx->alloc_end;
+ block += abs(block->val))
{
- if (this->val < 0)
+ check_block_length(ctx, block);
+ if (block->val < 0)
continue;
- crc_slot = (union buflib_data*)
- ((union buflib_data*)this[1].handle)->alloc - 1;
- metadata_size = (crc_slot - this)*sizeof(union buflib_data);
- crc = crc_32((void *)this, metadata_size, 0xffffffff);
-
- if (crc != crc_slot->crc)
- buflib_panic(ctx, "crc mismatch: 0x%08x, expected: 0x%08x",
- (unsigned int)crc, (unsigned int)crc_slot->crc);
+ check_block_handle(ctx, block);
}
}
#endif
-#ifdef BUFLIB_DEBUG_BLOCKS
-void buflib_print_allocs(struct buflib_context *ctx,
- void (*print)(int, const char*))
+#ifdef BUFLIB_DEBUG_PRINT
+int buflib_get_num_blocks(struct buflib_context *ctx)
{
- union buflib_data *this, *end = ctx->handle_table;
- char buf[128];
- for(this = end - 1; this >= ctx->last_handle; this--)
+ int i = 0;
+
+ for(union buflib_data *block = ctx->buf_start;
+ block < ctx->alloc_end;
+ block += abs(block->val))
{
- if (!this->alloc) continue;
-
- int handle_num;
- const char *name;
- union buflib_data *block_start, *alloc_start;
- intptr_t alloc_len;
-
- handle_num = end - this;
- alloc_start = buflib_get_data(ctx, handle_num);
- name = buflib_get_name(ctx, handle_num);
- block_start = (union buflib_data*)name - 3;
- alloc_len = block_start->val * sizeof(union buflib_data);
-
- snprintf(buf, sizeof(buf),
- "%s(%d):\t%p\n"
- " \t%p\n"
- " \t%ld\n",
- name?:"(null)", handle_num, block_start, alloc_start, alloc_len);
- /* handle_num is 1-based */
- print(handle_num - 1, buf);
+ check_block_length(ctx, block);
+ ++i;
}
+
+ return i;
}
-void buflib_print_blocks(struct buflib_context *ctx,
- void (*print)(int, const char*))
+bool buflib_print_block_at(struct buflib_context *ctx, int block_num,
+ char *buf, size_t bufsize)
{
- char buf[128];
- int i = 0;
- for(union buflib_data* this = ctx->buf_start;
- this < ctx->alloc_end;
- this += abs(this->val))
+ for(union buflib_data *block = ctx->buf_start;
+ block < ctx->alloc_end;
+ block += abs(block->val))
{
- snprintf(buf, sizeof(buf), "%8p: val: %4ld (%s)",
- this, this->val,
- this->val > 0? this[3].name:"<unallocated>");
- print(i++, buf);
+ check_block_length(ctx, block);
+
+ if (block_num-- == 0)
+ {
+ snprintf(buf, bufsize, "%8p: val: %4ld (%sallocated)",
+ block, (long)block->val,
+ block->val > 0 ? "" : "un");
+ return true;
+ }
}
+
+ if (bufsize > 0)
+ *buf = '\0';
+ return false;
}
#endif
-#ifdef BUFLIB_DEBUG_BLOCK_SINGLE
-int buflib_get_num_blocks(struct buflib_context *ctx)
+static void check_block_length(struct buflib_context *ctx,
+ union buflib_data *block)
{
- int i = 0;
- for(union buflib_data* this = ctx->buf_start;
- this < ctx->alloc_end;
- this += abs(this->val))
+ if (BUFLIB_PARANOIA & PARANOIA_CHECK_LENGTH)
{
- i++;
+ intptr_t length = block[BUFLIB_IDX_LEN].val;
+
+ /* Check the block length does not pass beyond the end */
+ if (length == 0 || block > ctx->alloc_end - abs(length))
+ {
+ buflib_panic(ctx, "block len wacky [%p]=%ld",
+ (void*)&block[BUFLIB_IDX_LEN], (long)length);
+ }
}
- return i;
}
-void buflib_print_block_at(struct buflib_context *ctx, int block_num,
- char* buf, size_t bufsize)
+static void check_block_handle(struct buflib_context *ctx,
+ union buflib_data *block)
{
- union buflib_data* this = ctx->buf_start;
- while(block_num > 0 && this < ctx->alloc_end)
+ if (BUFLIB_PARANOIA & PARANOIA_CHECK_BLOCK_HANDLE)
{
- this += abs(this->val);
- block_num -= 1;
+ intptr_t length = block[BUFLIB_IDX_LEN].val;
+ union buflib_data *h_entry = block[BUFLIB_IDX_HANDLE].handle;
+
+ /* Check the handle pointer is properly aligned */
+ /* TODO: Can we ensure the compiler doesn't optimize this out?
+ * I dunno, maybe the compiler can assume the pointer is always
+ * properly aligned due to some C standard voodoo?? */
+ if (!IS_ALIGNED((uintptr_t)h_entry, alignof(*h_entry)))
+ {
+ buflib_panic(ctx, "handle unaligned [%p]=%p",
+ &block[BUFLIB_IDX_HANDLE], h_entry);
+ }
+
+ /* Check the pointer is actually inside the handle table */
+ if (h_entry < ctx->last_handle || h_entry >= ctx->handle_table)
+ {
+ buflib_panic(ctx, "handle out of bounds [%p]=%p",
+ &block[BUFLIB_IDX_HANDLE], h_entry);
+ }
+
+ /* Now check the allocation is within the block.
+ * This is stricter than check_handle(). */
+ void *alloc = h_entry->alloc;
+ void *alloc_begin = block;
+ void *alloc_end = block + length;
+ /* buflib allows zero length allocations, so alloc_end is inclusive */
+ if (alloc < alloc_begin || alloc > alloc_end)
+ {
+ buflib_panic(ctx, "alloc outside block [%p]=%p, %p-%p",
+ h_entry, alloc, alloc_begin, alloc_end);
+ }
}
- snprintf(buf, bufsize, "%8p: val: %4ld (%s)",
- this, (long)this->val,
- this->val > 0? this[3].name:"<unallocated>");
}
-
-#endif
diff --git a/firmware/chunk_alloc.c b/firmware/chunk_alloc.c
new file mode 100644
index 0000000000..02cc7a056c
--- /dev/null
+++ b/firmware/chunk_alloc.c
@@ -0,0 +1,319 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2023 by William Wilgus
+ *
+ * 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.
+ *
+ ****************************************************************************/
+/* [chunk_alloc] allows arrays (or any data) to be allocated in smaller chunks */
+
+#include "chunk_alloc.h"
+#include "panic.h"
+
+//#define LOGF_ENABLE
+#include "logf.h"
+
+#ifndef MAX
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#endif
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+/*Note on offsets returned
+ * variable data will have variable offsets you need to
+ * store these as [chunk_alloc] doesn't keep track
+ * if you have a fixed size for each alloc you can do
+ * offset / sizeof(data) or index * sizeof(data) to convert
+ */
+
+struct chunk_alloc
+{
+ int handle; /* data handle of buflib allocated bytes */
+ size_t max_start_offset; /* start of last allocation */
+};
+
+#define CHUNK_ARRSZ(n) (sizeof(struct chunk_alloc) * n)
+
+static struct chunk_alloc* get_chunk_array(struct buflib_context *ctx, int handle)
+{
+ return (struct chunk_alloc*)buflib_get_data_pinned(ctx, handle);
+}
+
+static void put_chunk_array(struct buflib_context *ctx, struct chunk_alloc *data)
+{
+ buflib_put_data_pinned(ctx, data);
+}
+
+/* shrink or grow chunk allocation
+ * chunks greater than max_chunks will be freed
+ * new allocs will default to chunk_size
+ * previous or current chunk < max_chunks will NOT be changed
+ * Returns true on success false on failure
+*/
+bool chunk_realloc(struct chunk_alloc_header *hdr,
+ size_t chunk_size, size_t max_chunks)
+{
+ struct buflib_context *ctx = hdr->context;
+ struct chunk_alloc *new_chunk = NULL;
+ struct chunk_alloc *old_chunk;
+ size_t min_chunk = 1;
+ int new_handle = 0;
+
+ if (max_chunks > hdr->count) /* need room for more chunks */
+ {
+ new_handle = buflib_alloc(ctx, CHUNK_ARRSZ(max_chunks));
+
+ if (new_handle <= 0)
+ {
+ logf("%s Error OOM %ld chunks", __func__, max_chunks);
+ return false;
+ }
+ new_chunk = get_chunk_array(ctx, new_handle);
+ /* ensure all chunks data is zeroed, we depend on it */
+ memset(new_chunk, 0, CHUNK_ARRSZ(max_chunks));
+ put_chunk_array(ctx, new_chunk);
+ }
+ if (hdr->chunk_handle > 0) /* handle existing chunk */
+ {
+ logf("%s %ld chunks => %ld chunks", __func__, hdr->count, max_chunks);
+
+ old_chunk = get_chunk_array(ctx, hdr->chunk_handle);
+
+ if (new_handle > 0) /* copy any valid old chunks to new */
+ {
+ min_chunk = MIN(max_chunks, hdr->current + 1);
+ logf("%s copying %ld chunks", __func__, min_chunk);
+ new_chunk = get_chunk_array(ctx, new_handle);
+ memcpy(new_chunk, old_chunk, CHUNK_ARRSZ(min_chunk));
+ put_chunk_array(ctx, new_chunk);
+ }
+ /* free any chunks that no longer fit */
+ for (size_t i = max_chunks; i <= hdr->current; i++)
+ {
+ logf("%s discarding chunk[%ld]", __func__, i);
+ buflib_free(ctx, old_chunk[i].handle);
+ }
+ put_chunk_array(ctx, old_chunk);
+
+ if (max_chunks < hdr->count && max_chunks > 0)
+ {
+ logf("%s shrink existing chunk array", __func__);
+ min_chunk = max_chunks;
+ buflib_shrink(ctx, hdr->chunk_handle,
+ NULL, CHUNK_ARRSZ(max_chunks));
+
+ new_handle = hdr->chunk_handle;
+ }
+ else
+ {
+ logf("%s free existing chunk array", __func__);
+ buflib_free(ctx, hdr->chunk_handle); /* free old chunk array */
+ }
+
+ hdr->current = min_chunk - 1;
+ }
+ else
+ {
+ logf("chunk_alloc_init %ld chunks", hdr->count);
+ }
+ hdr->cached_chunk.handle = 0; /* reset last chunk to force new lookup */
+ hdr->chunk_handle = new_handle;
+ hdr->chunk_size = chunk_size;
+ hdr->count = max_chunks;
+
+ return true;
+}
+
+/* frees all allocations */
+void chunk_alloc_free(struct chunk_alloc_header *hdr)
+{
+ logf("%s freeing %ld chunks", __func__, hdr->count);
+ chunk_realloc(hdr, 0, 0);
+}
+
+/* initialize chunk allocator
+ * chunk_size specifies initial size of each chunk
+ * a single allocation CAN be larger than this
+ * max_chunks * chunk_size is the total expected size of the buffer
+ * more data will not be allocated once all chunks used
+ * Returns true on success or false on failure
+*/
+bool chunk_alloc_init(struct chunk_alloc_header *hdr,
+ struct buflib_context *ctx,
+ size_t chunk_size, size_t max_chunks)
+{
+ /* initialize header */
+ memset(hdr, 0, sizeof(struct chunk_alloc_header));
+ hdr->context = ctx;
+
+ return chunk_realloc(hdr, chunk_size, max_chunks);
+}
+
+/* shrink current chunk to size used */
+static void finalize(struct chunk_alloc_header *hdr, struct chunk_alloc *chunk_array)
+{
+ /* Note calling functions check if chunk_bytes_free > 0 */
+ size_t idx = hdr->current;
+ if (idx >= hdr->count)
+ return;
+ int handle = chunk_array[idx].handle;
+ struct buflib_context *ctx = hdr->context;
+
+ hdr->chunk_bytes_total -= hdr->chunk_bytes_free;
+ hdr->chunk_bytes_free = 0;
+
+ buflib_shrink(ctx, handle, NULL, hdr->chunk_bytes_total);
+
+ logf("%s shrink hdr idx[%ld] offset[%ld]: new size: %ld",
+ __func__, idx, chunk_array[idx].max_start_offset, hdr->chunk_bytes_total);
+}
+
+/* shrink current chunk to size used */
+void chunk_alloc_finalize(struct chunk_alloc_header *hdr)
+{
+ if (hdr->chunk_bytes_free > 0)
+ {
+ struct chunk_alloc *chunk;
+ chunk = get_chunk_array(hdr->context, hdr->chunk_handle);
+ finalize(hdr, chunk);
+ put_chunk_array(hdr->context, chunk);
+ }
+}
+
+/* allocates from current chunk if size > bytes remaining
+ * current chunk shrinks to size used and a new chunk is allocated
+ * returns virtual offset on success or CHUNK_ALLOC_INVALID on error
+*/
+size_t chunk_alloc(struct chunk_alloc_header *hdr, size_t size)
+{
+ size_t virtual_offset = CHUNK_ALLOC_INVALID;
+ size_t idx = hdr->current;
+ int handle = hdr->chunk_handle;
+ struct buflib_context *ctx = hdr->context;
+
+ struct chunk_alloc *chunk = get_chunk_array(ctx, handle);
+
+ while (size > 0)
+ {
+ if (idx >= hdr->count)
+ {
+ logf("%s Error OOM -- out of chunks", __func__);
+ break; /* Out of chunks */
+ }
+ hdr->current = idx;
+
+ if(chunk[idx].handle <= 0) /* need to make an new allocation */
+ {
+ size_t new_alloc_size = MAX(size, hdr->chunk_size);
+
+ chunk[idx].handle = buflib_alloc(ctx, new_alloc_size);
+
+ if (chunk[idx].handle <= 0)
+ {
+ logf("%s Error OOM", __func__);
+ break; /* OOM */
+ }
+
+ hdr->chunk_bytes_total = new_alloc_size;
+ hdr->chunk_bytes_free = new_alloc_size;
+
+ chunk[idx].max_start_offset =
+ (idx > 0 ? (chunk[idx - 1].max_start_offset) : 0);
+
+ logf("%s New alloc hdr idx[%ld] offset[%ld]: available: %ld",
+ __func__, idx, chunk[idx].max_start_offset, new_alloc_size);
+ }
+
+ if(size <= hdr->chunk_bytes_free) /* request will fit */
+ {
+ virtual_offset = chunk[idx].max_start_offset;
+ chunk[idx].max_start_offset += size;
+ hdr->chunk_bytes_free -= size;
+
+ if (hdr->cached_chunk.handle == chunk[idx].handle)
+ hdr->cached_chunk.max_offset = chunk[idx].max_start_offset;
+
+ /*logf("%s hdr idx[%ld] offset[%ld] size: %ld",
+ __func__, idx, offset, size);*/
+
+ break; /* Success */
+ }
+ else if (hdr->chunk_bytes_free > 0) /* shrink the current chunk */
+ {
+ finalize(hdr, chunk);
+ }
+ idx++;
+ }
+
+ put_chunk_array(ctx, chunk);
+ return virtual_offset;
+}
+
+/* retrieves chunk given virtual offset
+ * returns actual offset
+*/
+static size_t chunk_get_at_offset(struct chunk_alloc_header *hdr, size_t offset)
+{
+ if ((hdr->cached_chunk.handle > 0)
+ && (offset >= hdr->cached_chunk.min_offset)
+ && (offset < hdr->cached_chunk.max_offset))
+ {
+ /* convert virtual offset to real internal offset */
+ return offset - hdr->cached_chunk.min_offset;
+ }
+
+ /* chunk isn't cached perform new lookup */
+ struct chunk_alloc *chunk = get_chunk_array(hdr->context, hdr->chunk_handle);
+ logf("%s search for offset[%ld]", __func__, offset);
+ for (size_t idx = hdr->current; idx < hdr->count; idx--)
+ {
+ size_t min_offset = (idx == 0 ? 0 : chunk[idx - 1].max_start_offset);
+ if (offset < chunk[idx].max_start_offset && offset >= min_offset)
+ {
+ logf("%s found hdr idx[%ld] min offset[%ld] max offset[%ld]",
+ __func__, idx, min_offset, chunk[idx].max_start_offset);
+
+ /* store found chunk */
+ hdr->cached_chunk.handle = chunk[idx].handle;
+ hdr->cached_chunk.max_offset = chunk[idx].max_start_offset;
+ hdr->cached_chunk.min_offset = min_offset;
+ put_chunk_array(hdr->context, chunk);
+ /* convert virtual offset to real internal offset */
+ return offset - hdr->cached_chunk.min_offset;
+ }
+ }
+ panicf("%s Error offset %d does not exist", __func__, (unsigned int)offset);
+ return CHUNK_ALLOC_INVALID;
+}
+
+/* get data - buffer chunk can't be moved while pinned
+ * multiple calls will up pin count so put should be called for each
+ * Returns data at offset
+*/
+void* chunk_get_data(struct chunk_alloc_header *hdr, size_t offset)
+{
+ size_t real = chunk_get_at_offset(hdr, offset);
+ logf("%s offset: %ld real: %ld", __func__, offset, real);
+ return buflib_get_data_pinned(hdr->context, hdr->cached_chunk.handle) + real;
+}
+
+/* release a pinned buffer, chunk can't be moved till pin count == 0 */
+void chunk_put_data(struct chunk_alloc_header *hdr, void* data, size_t offset)
+{
+ size_t real = chunk_get_at_offset(hdr, offset);
+ buflib_put_data_pinned(hdr->context, data - real);
+}
diff --git a/firmware/common/adler32.c b/firmware/common/adler32.c
new file mode 100644
index 0000000000..419eb02e44
--- /dev/null
+++ b/firmware/common/adler32.c
@@ -0,0 +1,97 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 James Buren (adaptations from tinf/zlib)
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+/* Copyright (c) 2003-2019 Joergen Ibsen
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must
+ * not claim that you wrote the original software. If you use this
+ * software in a product, an acknowledgment in the product
+ * documentation would be appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must
+ * not be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source
+ * distribution.
+ */
+
+#include "adler32.h"
+#include "system.h"
+
+/* adler_32 (derived from tinf adler32 which was taken from zlib)
+ * Adler-32 algorithm taken from the zlib source, which is
+ * Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler
+ */
+uint32_t adler_32(const void *src, uint32_t len, uint32_t adler32)
+{
+ const unsigned char *buf = (const unsigned char *)src;
+ uint32_t s1 = (adler32 & 0xffff);
+ uint32_t s2 = (adler32 >> 16);
+
+ enum {
+ A32_BASE = 65521,
+ A32_NMAX = 5552,
+ };
+
+ while (len > 0) {
+ uint32_t k = MIN(len, A32_NMAX);
+ uint32_t i;
+
+ for (i = k / 16; i; --i, buf += 16) {
+ s2 += s1 += buf[0];
+ s2 += s1 += buf[1];
+ s2 += s1 += buf[2];
+ s2 += s1 += buf[3];
+ s2 += s1 += buf[4];
+ s2 += s1 += buf[5];
+ s2 += s1 += buf[6];
+ s2 += s1 += buf[7];
+ s2 += s1 += buf[8];
+ s2 += s1 += buf[9];
+ s2 += s1 += buf[10];
+ s2 += s1 += buf[11];
+ s2 += s1 += buf[12];
+ s2 += s1 += buf[13];
+ s2 += s1 += buf[14];
+ s2 += s1 += buf[15];
+ }
+
+ for (i = k % 16; i; --i) {
+ s1 += *buf++;
+ s2 += s1;
+ }
+
+ s1 %= A32_BASE;
+ s2 %= A32_BASE;
+
+ len -= k;
+ }
+
+ return (s1 | (s2 << 16));
+}
diff --git a/firmware/common/crc32-mi4.c b/firmware/common/crc32-mi4.c
deleted file mode 100644
index e1a5769018..0000000000
--- a/firmware/common/crc32-mi4.c
+++ /dev/null
@@ -1,110 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id: crc32.c 10464 2006-08-05 20:19:10Z miipekk $
- *
- * Copyright (C) 2007 Barry Wardell
- *
- * 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.
- *
- ****************************************************************************/
-
-/*
- * We can't use the CRC32 implementation in the firmware library as it uses a
- * different polynomial. The polynomial needed is 0xEDB88320L
- *
- * CRC32 implementation taken from:
- *
- * efone - Distributed internet phone system.
- *
- * (c) 1999,2000 Krzysztof Dabrowski
- * (c) 1999,2000 ElysiuM deeZine
- *
- * 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.
- *
- */
-
-/* based on implementation by Finn Yannick Jacobs */
-
-#include "crc32-mi4.h"
-
-/* crc_tab[] -- this crcTable is being build by chksum_crc32GenTab().
- * so make sure, you call it before using the other
- * functions!
- */
-static const unsigned int crc_tab[256] = {
- 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
- 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
- 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
- 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
- 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
- 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
- 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
- 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
- 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
- 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
- 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
- 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
- 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
- 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
- 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
- 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
- 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
- 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
- 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
- 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
- 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
- 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
- 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
- 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
- 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
- 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
- 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
- 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
- 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
- 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
- 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
- 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
- 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
- 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
- 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
- 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
- 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
- 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
- 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
- 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
- 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
- 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
- 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
-};
-
-/* chksum_crc() -- to a given block, this one calculates the
- * crc32-checksum until the length is
- * reached. the crc32-checksum will be
- * the result.
- */
-unsigned int chksum_crc32 (unsigned char *block, unsigned int length)
-{
- register unsigned long crc;
- unsigned long i;
-
- crc = 0;
- for (i = 0; i < length; i++)
- {
- crc = ((crc >> 8) & 0x00FFFFFF) ^ crc_tab[(crc ^ *block++) & 0xFF];
- }
- return (crc);
-}
diff --git a/firmware/common/crc32.c b/firmware/common/crc32.c
index c8c70e415c..c8ed8f5350 100644
--- a/firmware/common/crc32.c
+++ b/firmware/common/crc32.c
@@ -19,12 +19,88 @@
*
****************************************************************************/
-/* Code copied from firmware_flash plugin. */
+/* Copyright (c) 2003-2019 Joergen Ibsen
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must
+ * not claim that you wrote the original software. If you use this
+ * software in a product, an acknowledgment in the product
+ * documentation would be appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must
+ * not be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source
+ * distribution.
+ */
#include "crc32.h"
+#include "config.h"
/* Tool function to calculate a CRC32 across some buffer */
/* third argument is either 0xFFFFFFFF to start or value from last piece */
+/* speed optimized version */
+#ifdef CRC32_FAST
+uint32_t crc_32(const void *src, uint32_t len, uint32_t crc32)
+{
+ const unsigned char *buf = (const unsigned char *)src;
+
+ /* polynomial 0x04C11DB7 - generated with help from pycrc (https://pycrc.org/index.html) */
+ static const unsigned crc32_lookup[256] = {
+ 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
+ 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
+ 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
+ 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
+ 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
+ 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
+ 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
+ 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,
+ 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
+ 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
+ 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
+ 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
+ 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,
+ 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
+ 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
+ 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,
+ 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
+ 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
+ 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,
+ 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
+ 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
+ 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
+ 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,
+ 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
+ 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
+ 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,
+ 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
+ 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
+ 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,
+ 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
+ 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,
+ 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
+ };
+
+ while (len--) {
+ crc32 = (crc32_lookup[((crc32 >> 24) ^ *buf)] ^ (crc32 << 8));
+ buf++;
+ }
+
+ return crc32;
+}
+#endif
+
+/* Tool function to calculate a CRC32 across some buffer */
+/* third argument is either 0xFFFFFFFF to start or value from last piece */
+/* space optimized version */
+#ifndef CRC32_FAST
uint32_t crc_32(const void *src, uint32_t len, uint32_t crc32)
{
const unsigned char *buf = (const unsigned char *)src;
@@ -60,4 +136,95 @@ uint32_t crc_32(const void *src, uint32_t len, uint32_t crc32)
return crc32;
}
+#endif
+
+/* Tool function to calculate a CRC32 (reversed polynomial) across some buffer */
+/* third argument is either the starting value or value from last piece */
+/* speed optimized version */
+#ifdef CRC32R_FAST
+uint32_t crc_32r(const void *src, uint32_t len, uint32_t crc32)
+{
+ const unsigned char *buf = (const unsigned char *)src;
+ /* crc_32r (derived from recent zlib crc32)
+ * CRC32 algorithm taken from the zlib source, which is
+ * Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler
+ */
+
+ /* reversed polynomial from other crc32 function -- 0xEDB88320 */
+ static const unsigned crc32_lookup[256] = {
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
+ 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
+ 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
+ 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+ 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
+ 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
+ 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+ 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
+ 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
+ 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
+ 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
+ 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
+ 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
+ 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+ 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
+ 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
+ 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+ 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
+ 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
+ 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
+ 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
+ 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
+ };
+
+ while (len--) {
+ crc32 = (crc32_lookup[(crc32 ^ *buf) & 0xff] ^ (crc32 >> 8));
+ buf++;
+ }
+
+ return crc32;
+}
+#endif
+
+/* Tool function to calculate a CRC32 (reversed polynomial) across some buffer */
+/* third argument is either the starting value or value from last piece */
+/* space optimized version */
+#ifndef CRC32R_FAST
+uint32_t crc_32r(const void *src, uint32_t len, uint32_t crc32)
+{
+ const unsigned char* buf = src;
+
+ /* crc_32r (derived from tinf crc32 which was taken from zlib)
+ * CRC32 algorithm taken from the zlib source, which is
+ * Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler
+ */
+
+ /* reversed polynomial from other crc32 function -- 0xEDB88320 */
+ static const unsigned crc32_lookup[16] =
+ {
+ 0x00000000, 0x1DB71064, 0x3B6E20C8, 0x26D930AC,
+ 0x76DC4190, 0x6B6B51F4, 0x4DB26158, 0x5005713C,
+ 0xEDB88320, 0xF00F9344, 0xD6D6A3E8, 0xCB61B38C,
+ 0x9B64C2B0, 0x86D3D2D4, 0xA00AE278, 0xBDBDF21C
+ };
+
+ for(uint32_t i = 0; i < len; i++)
+ {
+ crc32 ^= buf[i];
+ crc32 = crc32_lookup[crc32 & 0x0F] ^ (crc32 >> 4);
+ crc32 = crc32_lookup[crc32 & 0x0F] ^ (crc32 >> 4);
+ }
+
+ return crc32;
+}
+#endif
diff --git a/firmware/common/diacritic.c b/firmware/common/diacritic.c
index 92c2400203..a21b3a40b1 100644
--- a/firmware/common/diacritic.c
+++ b/firmware/common/diacritic.c
@@ -232,7 +232,10 @@ bool is_diacritic(const unsigned short char_code, bool *is_rtl)
/* Add MRU entry */
if (mru_len < MRU_MAX_LEN)
+ {
+ diacritic_mru[mru_len] = i;
mru_len++;
+ }
Found:
diff --git a/firmware/common/dir.c b/firmware/common/dir.c
index f89129ae34..9a78d910a7 100644
--- a/firmware/common/dir.c
+++ b/firmware/common/dir.c
@@ -26,18 +26,16 @@
#include "debug.h"
#include "dir.h"
#include "pathfuncs.h"
+#include "timefuncs.h"
#include "fileobj_mgr.h"
-#include "dircache_redirect.h"
+#include "rb_namespace.h"
/* structure used for open directory streams */
static struct dirstr_desc
{
struct filestr_base stream; /* basic stream info (first!) */
- struct dirscan_info scan; /* directory scan cursor */
+ struct ns_scan_info scan; /* directory scan cursor */
struct dirent entry; /* current parsed entry information */
-#ifdef HAVE_MULTIVOLUME
- int volumecounter; /* counter for root volume entries */
-#endif
} open_streams[MAX_OPEN_DIRS];
/* check and return a struct dirstr_desc* from a DIR* */
@@ -47,7 +45,7 @@ static struct dirstr_desc * get_dirstr(DIR *dirp)
if (!PTR_IN_ARRAY(open_streams, dir, MAX_OPEN_DIRS))
dir = NULL;
- else if (dir->stream.flags & FDO_BUSY)
+ else if (dir->stream.flags & (FDO_BUSY|FD_VALID))
return dir;
int errnum;
@@ -104,50 +102,6 @@ static struct dirstr_desc * alloc_dirstr(void)
return NULL;
}
-#ifdef HAVE_MULTIVOLUME
-static int readdir_volume_inner(struct dirstr_desc *dir, struct dirent *entry)
-{
- /* Volumes (secondary file systems) get inserted into the system root
- * directory. If the path specified volume 0, enumeration will not
- * include other volumes, but just its own files and directories.
- *
- * Fake special directories, which don't really exist, that will get
- * redirected upon opendir()
- */
- while (++dir->volumecounter < NUM_VOLUMES)
- {
- /* on the system root */
- if (!fat_ismounted(dir->volumecounter))
- continue;
-
- get_volume_name(dir->volumecounter, entry->d_name);
- dir->entry.info.attr = ATTR_MOUNT_POINT;
- dir->entry.info.size = 0;
- dir->entry.info.wrtdate = 0;
- dir->entry.info.wrttime = 0;
- return 1;
- }
-
- /* do normal directory entry fetching */
- return 0;
-}
-#endif /* HAVE_MULTIVOLUME */
-
-static inline int readdir_volume(struct dirstr_desc *dir,
- struct dirent *entry)
-{
-#ifdef HAVE_MULTIVOLUME
- /* fetch virtual volume entries? */
- if (dir->volumecounter < NUM_VOLUMES)
- return readdir_volume_inner(dir, entry);
-#endif /* HAVE_MULTIVOLUME */
-
- /* do normal directory entry fetching */
- return 0;
- (void)dir; (void)entry;
-}
-
-
/** POSIX interface **/
/* open a directory */
@@ -165,21 +119,13 @@ DIR * opendir(const char *dirname)
if (!dir)
FILE_ERROR(EMFILE, RC);
- rc = open_stream_internal(dirname, FF_DIR, &dir->stream, NULL);
+ rc = ns_open_stream(dirname, FF_DIR, &dir->stream, &dir->scan);
if (rc < 0)
{
DEBUGF("Open failed: %d\n", rc);
FILE_ERROR(ERRNO, RC);
}
-#ifdef HAVE_MULTIVOLUME
- /* volume counter is relevant only to the system root */
- dir->volumecounter = rc > 1 ? 0 : INT_MAX;
-#endif /* HAVE_MULTIVOLUME */
-
- fat_rewind(&dir->stream.fatstr);
- rewinddir_dirent(&dir->scan);
-
dirp = (DIR *)dir;
file_error:
file_internal_unlock_WRITER();
@@ -204,7 +150,7 @@ int closedir(DIR *dirp)
FILE_ERROR(EBADF, -2);
}
- rc = close_stream_internal(&dir->stream);
+ rc = ns_close_stream(&dir->stream);
if (rc < 0)
FILE_ERROR(ERRNO, rc * 10 - 3);
@@ -222,16 +168,11 @@ struct dirent * readdir(DIR *dirp)
struct dirent *res = NULL;
- int rc = readdir_volume(dir, &dir->entry);
- if (rc == 0)
- {
- rc = readdir_dirent(&dir->stream, &dir->scan, &dir->entry);
- if (rc < 0)
- FILE_ERROR(EIO, RC);
- }
-
+ int rc = ns_readdir_dirent(&dir->stream, &dir->scan, &dir->entry);
if (rc > 0)
res = &dir->entry;
+ else if (rc < 0)
+ FILE_ERROR(EIO, RC);
file_error:
RELEASE_DIRSTR(READER, dir);
@@ -258,13 +199,9 @@ int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
if (!dir)
FILE_ERROR_RETURN(ERRNO, -1);
- int rc = readdir_volume(dir, entry);
- if (rc == 0)
- {
- rc = readdir_dirent(&dir->stream, &dir->scan, entry);
- if (rc < 0)
- FILE_ERROR(EIO, rc * 10 - 4);
- }
+ int rc = ns_readdir_dirent(&dir->stream, &dir->scan, entry);
+ if (rc < 0)
+ FILE_ERROR(EIO, rc * 10 - 4);
file_error:
RELEASE_DIRSTR(READER, dir);
@@ -288,12 +225,7 @@ void rewinddir(DIR *dirp)
if (!dir)
FILE_ERROR_RETURN(ERRNO);
- rewinddir_dirent(&dir->scan);
-
-#ifdef HAVE_MULTIVOLUME
- if (dir->volumecounter != INT_MAX)
- dir->volumecounter = 0;
-#endif /* HAVE_MULTIVOLUME */
+ ns_dirscan_rewind(&dir->scan);
RELEASE_DIRSTR(READER, dir);
}
@@ -406,9 +338,15 @@ struct dirinfo dir_get_info(DIR *dirp, struct dirent *entry)
{
.attribute = entry->info.attr,
.size = entry->info.size,
- .mtime = fattime_mktime(entry->info.wrtdate, entry->info.wrttime),
+ .mtime = dostime_mktime(entry->info.wrtdate, entry->info.wrttime),
};
file_error:
return (struct dirinfo){ .attribute = 0 };
}
+
+const char* root_realpath(void)
+{
+ /* Native only, for APP and SIM see respective filesystem-.c files */
+ return root_get_realpath(); /* rb_namespace.c */
+}
diff --git a/firmware/common/dircache.c b/firmware/common/dircache.c
index 589986911c..c274b6c62c 100644
--- a/firmware/common/dircache.c
+++ b/firmware/common/dircache.c
@@ -249,7 +249,6 @@ static struct dircache_runinfo
/* cache buffer info */
int handle; /* buflib buffer handle */
size_t bufsize; /* size of buflib allocation - 1 */
- int buflocked; /* don't move due to other allocs */
union {
void *p; /* address of buffer - ENTRYSIZE */
struct dircache_entry *pentry; /* alias of .p to assist entry resolution */
@@ -329,29 +328,9 @@ static inline void dumpster_clean_buffer(void *p, size_t size)
*/
static int move_callback(int handle, void *current, void *new)
{
- if (dircache_runinfo.buflocked)
- return BUFLIB_CB_CANNOT_MOVE;
-
+ (void)handle; (void)current;
dircache_runinfo.p = new - ENTRYSIZE;
-
return BUFLIB_CB_OK;
- (void)handle; (void)current;
-}
-
-/**
- * add a "don't move" lock count
- */
-static inline void buffer_lock(void)
-{
- dircache_runinfo.buflocked++;
-}
-
-/**
- * remove a "don't move" lock count
- */
-static inline void buffer_unlock(void)
-{
- dircache_runinfo.buflocked--;
}
@@ -500,7 +479,7 @@ static void binding_dissolve_volume(struct dircache_runinfo_volume *dcrivolp)
static int alloc_cache(size_t size)
{
/* pad with one extra-- see alloc_name() and free_name() */
- return core_alloc_ex("dircache", size + 1, &dircache_runinfo.ops);
+ return core_alloc_ex(size + 1, &dircache_runinfo.ops);
}
/**
@@ -530,14 +509,14 @@ static void set_buffer(int handle, size_t size)
/**
* remove the allocation from dircache control and return the handle
+ * Note that dircache must not be using the buffer!
*/
static int reset_buffer(void)
{
int handle = dircache_runinfo.handle;
if (handle > 0)
{
- /* don't mind .p; it might get changed by the callback even after
- this call; buffer presence is determined by the following: */
+ /* don't mind .p; buffer presence is determined by the following: */
dircache_runinfo.handle = 0;
dircache_runinfo.bufsize = 0;
}
@@ -1473,7 +1452,7 @@ static void sab_process_volume(struct dircache_volume *dcvolp)
*/
int dircache_readdir_dirent(struct filestr_base *stream,
struct dirscan_info *scanp,
- struct dirent *entry)
+ struct DIRENT *entry)
{
struct file_base_info *dirinfop = stream->infop;
@@ -1728,8 +1707,8 @@ static int sab_process_dir(struct dircache_entry *ce)
/* save current paths size */
int pathpos = strlen(sab_path);
/* append entry */
- strlcpy(&sab_path[pathpos], "/", sizeof(sab_path) - pathpos);
- strlcpy(&sab_path[pathpos+1], entry->d_name, sizeof(sab_path) - pathpos - 1);
+ strmemccpy(&sab_path[pathpos], "/", sizeof(sab_path) - pathpos);
+ strmemccpy(&sab_path[pathpos+1], entry->d_name, sizeof(sab_path) - pathpos - 1);
int rc = sab_process_dir(ce->down);
/* restore path */
@@ -1756,11 +1735,11 @@ static int sab_process_dir(struct dircache_entry *ce)
static int sab_process_volume(IF_MV(int volume,) struct dircache_entry *ce)
{
memset(ce, 0, sizeof(struct dircache_entry));
- strlcpy(sab_path, "/", sizeof sab_path);
+ strmemccpy(sab_path, "/", sizeof sab_path);
return sab_process_dir(ce);
}
-int dircache_readdir_r(struct dircache_dirscan *dir, struct dirent *result)
+int dircache_readdir_r(struct dircache_dirscan *dir, struct DIRENT *result)
{
if (dircache_state != DIRCACHE_READY)
return readdir_r(dir->###########3, result, &result);
@@ -1776,7 +1755,7 @@ int dircache_readdir_r(struct dircache_dirscan *dir, struct dirent *result)
dir->scanidx = ce - dircache_root;
- strlcpy(result->d_name, ce->d_name, sizeof (result->d_name));
+ strmemccpy(result->d_name, ce->d_name, sizeof (result->d_name));
result->info = ce->dirinfo;
return 1;
@@ -1857,7 +1836,7 @@ static void reset_cache(void)
*/
static void build_volumes(void)
{
- buffer_lock();
+ core_pin(dircache_runinfo.handle);
for (int i = 0; i < NUM_VOLUMES; i++)
{
@@ -1903,12 +1882,15 @@ static void build_volumes(void)
logf("Done, %ld KiB used", dircache.size / 1024);
- buffer_unlock();
+ if (dircache_runinfo.handle > 0) /* dircache may have been disabled */
+ core_unpin(dircache_runinfo.handle);
}
/**
* allocate buffer and return whether or not a synchronous build should take
* place; if 'realloced' is NULL, it's just a query about what will happen
+ *
+ * Note this must be called with the dircache_lock() active.
*/
static int prepare_build(bool *realloced)
{
@@ -1958,17 +1940,14 @@ static int prepare_build(bool *realloced)
*realloced = true;
reset_cache();
- buffer_lock();
-
int handle = reset_buffer();
- dircache_unlock();
+ dircache_unlock(); /* release lock held by caller */
- if (handle > 0)
- core_free(handle);
+ core_free(handle);
handle = alloc_cache(size);
- dircache_lock();
+ dircache_lock(); /* reacquire lock */
if (dircache_runinfo.suspended && handle > 0)
{
@@ -1980,13 +1959,9 @@ static int prepare_build(bool *realloced)
}
if (handle <= 0)
- {
- buffer_unlock();
return -1;
- }
set_buffer(handle, size);
- buffer_unlock();
return syncbuild;
}
@@ -2164,8 +2139,7 @@ static void dircache_suspend_internal(bool freeit)
dircache_unlock();
- if (handle > 0)
- core_free(handle);
+ core_free(handle);
thread_wait(thread_id);
@@ -2386,9 +2360,9 @@ void dircache_fileop_create(struct file_base_info *dirinfop,
if ((dinp->attr & ATTR_DIRECTORY) && !is_dotdir_name(basename))
{
/* scan-in the contents of the new directory at this level only */
- buffer_lock();
+ core_pin(dircache_runinfo.handle);
sab_process_dir(infop, false);
- buffer_unlock();
+ core_unpin(dircache_runinfo.handle);
}
}
@@ -2541,13 +2515,10 @@ static ssize_t get_path_sub(int idx, struct get_path_sub_data *data)
cename = "";
#ifdef HAVE_MULTIVOLUME
+ /* prepend the volume specifier */
int volume = IF_MV_VOL(-idx - 1);
- if (volume > 0)
- {
- /* prepend the volume specifier for volumes > 0 */
- cename = alloca(VOL_MAX_LEN+1);
- get_volume_name(volume, cename);
- }
+ cename = alloca(VOL_MAX_LEN+1);
+ get_volume_name(volume, cename);
#endif /* HAVE_MULTIVOLUME */
data->serialhash = dc_hash_serialnum(get_idx_dcvolp(idx)->serialnum,
@@ -2920,7 +2891,7 @@ void dircache_dump(void)
if (dircache_runinfo.handle)
{
- buffer_lock();
+ core_pin(dircache_runinfo.handle);
/* bin */
write(fdbin, dircache_runinfo.p + ENTRYSIZE,
@@ -2956,7 +2927,7 @@ void dircache_dump(void)
FOR_EACH_CACHE_ENTRY(ce)
{
#ifdef DIRCACHE_NATIVE
- time_t mtime = fattime_mktime(ce->wrtdate, ce->wrttime);
+ time_t mtime = dostime_mktime(ce->wrtdate, ce->wrttime);
#else
time_t mtime = ce->mtime;
#endif
@@ -2990,7 +2961,7 @@ void dircache_dump(void)
tm.tm_hour, tm.tm_min, tm.tm_sec);
}
- buffer_unlock();
+ core_unpin(dircache_runinfo.handle);
}
dircache_unlock();
@@ -3109,7 +3080,6 @@ int dircache_load(void)
}
dircache_lock();
- buffer_lock();
if (!dircache_is_clean(false))
goto error;
@@ -3118,6 +3088,7 @@ int dircache_load(void)
dircache = maindata.dircache;
set_buffer(handle, bufsize);
+ core_pin(handle);
hasbuffer = true;
/* convert back to in-RAM representation */
@@ -3172,17 +3143,17 @@ int dircache_load(void)
dircache_enable_internal(false);
/* cache successfully loaded */
+ core_unpin(handle);
logf("Done, %ld KiB used", dircache.size / 1024);
rc = 0;
error:
if (rc < 0 && hasbuffer)
reset_buffer();
- buffer_unlock();
dircache_unlock();
error_nolock:
- if (rc < 0 && handle > 0)
+ if (rc < 0)
core_free(handle);
if (fd >= 0)
@@ -3204,8 +3175,9 @@ int dircache_save(void)
if (fd < 0)
return -1;
+ /* it seems the handle *must* exist if this function is called */
dircache_lock();
- buffer_lock();
+ core_pin(dircache_runinfo.handle);
int rc = -1;
@@ -3274,7 +3246,7 @@ int dircache_save(void)
that makes what was saved completely invalid */
rc = 0;
error:
- buffer_unlock();
+ core_unpin(dircache_runinfo.handle);
dircache_unlock();
if (rc < 0)
diff --git a/firmware/common/disk.c b/firmware/common/disk.c
index 51d033b678..2fec38995a 100644
--- a/firmware/common/disk.c
+++ b/firmware/common/disk.c
@@ -27,7 +27,7 @@
#include "disk_cache.h"
#include "fileobj_mgr.h"
#include "dir.h"
-#include "dircache_redirect.h"
+#include "rb_namespace.h"
#include "disk.h"
#if defined(HAVE_BOOTDATA) && !defined(SIMULATOR) && !defined(BOOTLOADER)
@@ -44,7 +44,7 @@
#define disk_writer_lock() file_internal_lock_WRITER()
#define disk_writer_unlock() file_internal_unlock_WRITER()
-/* Partition table entry layout:
+/* "MBR" Partition table entry layout:
-----------------------
0: 0x80 - active
1: starting head
@@ -58,6 +58,16 @@
12-15: nr of sectors in partition
*/
+#define BYTES2INT64(array, pos) \
+ (((uint64_t)array[pos+0] << 0) | \
+ ((uint64_t)array[pos+1] << 8) | \
+ ((uint64_t)array[pos+2] << 16) | \
+ ((uint64_t)array[pos+3] << 24) | \
+ ((uint64_t)array[pos+4] << 32) | \
+ ((uint64_t)array[pos+5] << 40) | \
+ ((uint64_t)array[pos+6] << 48) | \
+ ((uint64_t)array[pos+7] << 56) )
+
#define BYTES2INT32(array, pos) \
(((uint32_t)array[pos+0] << 0) | \
((uint32_t)array[pos+1] << 8) | \
@@ -65,25 +75,40 @@
((uint32_t)array[pos+3] << 24))
#define BYTES2INT16(array, pos) \
- (((uint32_t)array[pos+0] << 0) | \
- ((uint32_t)array[pos+1] << 8))
+ (((uint16_t)array[pos+0] << 0) | \
+ ((uint16_t)array[pos+1] << 8))
+
+static struct partinfo part[NUM_DRIVES*MAX_PARTITIONS_PER_DRIVE];
+static struct volumeinfo volumes[NUM_VOLUMES];
-/* space for 4 partitions on 2 drives */
-static struct partinfo part[NUM_DRIVES*4];
-/* mounted to which drive (-1 if none) */
-static int vol_drive[NUM_VOLUMES];
+/* check if the entry points to a free volume */
+static bool is_free_volume(const struct volumeinfo *vi)
+{
+ return vi->drive < 0;
+}
+
+/* mark a volume entry as free */
+static void mark_free_volume(struct volumeinfo *vi)
+{
+ vi->drive = -1;
+ vi->partition = -1;
+}
static int get_free_volume(void)
{
for (int i = 0; i < NUM_VOLUMES; i++)
- {
- if (vol_drive[i] == -1) /* unassigned? */
+ if (is_free_volume(&volumes[i]))
return i;
- }
return -1; /* none found */
}
+static void init_volume(struct volumeinfo *vi, int drive, int part)
+{
+ vi->drive = drive;
+ vi->partition = part;
+}
+
#ifdef MAX_LOG_SECTOR_SIZE
static int disk_sector_multiplier[NUM_DRIVES] =
{ [0 ... NUM_DRIVES-1] = 1 };
@@ -120,12 +145,13 @@ bool disk_init(IF_MD_NONVOID(int drive))
/* For each drive, start at a different position, in order not to
destroy the first entry of drive 0. That one is needed to calculate
config sector position. */
- struct partinfo *pinfo = &part[IF_MD_DRV(drive)*4];
+ struct partinfo *pinfo = &part[IF_MD_DRV(drive)*MAX_PARTITIONS_PER_DRIVE];
+ uint8_t is_gpt = 0;
disk_writer_lock();
/* parse partitions */
- for (int i = 0; i < 4; i++)
+ for (int i = 0; i < MAX_PARTITIONS_PER_DRIVE && i < 4; i++)
{
unsigned char* ptr = sector + 0x1be + 16*i;
pinfo[i].type = ptr[4];
@@ -140,8 +166,115 @@ bool disk_init(IF_MD_NONVOID(int drive))
{
/* not handled yet */
}
- }
+ if (pinfo[i].type == PARTITION_TYPE_GPT_GUARD) {
+ is_gpt = 1;
+ }
+ }
+
+ while (is_gpt) {
+ /* Re-start partition parsing using GPT */
+ uint64_t part_lba;
+ uint32_t part_entries;
+ uint32_t part_entry_size;
+ unsigned char* ptr = sector;
+
+ storage_read_sectors(IF_MD(drive,) 1, 1, sector);
+
+ part_lba = BYTES2INT64(ptr, 0);
+ if (part_lba != 0x5452415020494645ULL) {
+ DEBUGF("GPT: Invalid signature\n");
+ break;
+ }
+ part_entry_size = BYTES2INT32(ptr, 8);
+ if (part_entry_size != 0x00010000) {
+ DEBUGF("GPT: Invalid version\n");
+ break;
+ }
+ part_entry_size = BYTES2INT32(ptr, 12);
+ if (part_entry_size != 0x5c) {
+ DEBUGF("GPT: Invalid header size\n");
+ break;
+ }
+ // XXX checksum header -- u32 @ offset 16
+ part_entry_size = BYTES2INT32(ptr, 24);
+ if (part_entry_size != 1) {
+ DEBUGF("GPT: Invalid header LBA\n");
+ break;
+ }
+
+ part_lba = BYTES2INT64(ptr, 72);
+ part_entries = BYTES2INT32(ptr, 80);
+ part_entry_size = BYTES2INT32(ptr, 84);
+
+ int part = 0;
+reload:
+ storage_read_sectors(IF_MD(drive,) part_lba, 1, sector);
+ uint8_t *pptr = ptr;
+ while (part < MAX_PARTITIONS_PER_DRIVE && part_entries) {
+ if (pptr - ptr >= SECTOR_SIZE) {
+ part_lba++;
+ goto reload;
+ }
+
+ /* Parse GPT entry. We only care about the "General Data" type, ie:
+ EBD0A0A2-B9E5-4433-87C0-68B6B72699C7
+ LE32 LE16 LE16 BE16 BE16
+ */
+ uint64_t tmp;
+ tmp = BYTES2INT32(pptr, 0);
+ if (tmp != 0xEBD0A0A2)
+ goto skip;
+ tmp = BYTES2INT16(pptr, 4);
+ if (tmp != 0xB9E5)
+ goto skip;
+ tmp = BYTES2INT16(pptr, 6);
+ if (tmp != 0x4433)
+ goto skip;
+ if (pptr[8] != 0x87 || pptr[9] != 0xc0)
+ goto skip;
+ if (pptr[10] != 0x68 || pptr[11] != 0xb6 || pptr[12] != 0xb7 ||
+ pptr[13] != 0x26 || pptr[14] != 0x99 || pptr[15] != 0xc7)
+ goto skip;
+
+ tmp = BYTES2INT64(pptr, 48); /* Flags */
+ if (tmp) {
+ DEBUGF("GPT: Skip parition with flags\n");
+ goto skip; /* Any flag makes us ignore this */
+ }
+ tmp = BYTES2INT64(pptr, 32); /* FIRST LBA */
+ if (tmp > UINT32_MAX) { // XXX revisit when we resize struct partinfo!
+ DEBUGF("GPT: partition starts after 2GiB mark\n");
+ goto skip;
+ }
+ if (tmp < 34) {
+ DEBUGF("GPT: Invalid start LBA\n");
+ goto skip;
+ }
+ pinfo[part].start = tmp;
+ tmp = BYTES2INT64(pptr, 40); /* LAST LBA */
+ if (tmp > UINT32_MAX) { // XXX revisit when we resize struct partinfo!
+ DEBUGF("GPT: partition ends after 2GiB mark\n");
+ goto skip;
+ }
+ if (tmp <= pinfo[part].start) {
+ DEBUGF("GPT: Invalid end LBA\n");
+ goto skip;
+ }
+ pinfo[part].size = tmp - pinfo[part].start + 1;
+ pinfo[part].type = PARTITION_TYPE_FAT32_LBA;
+
+ DEBUGF("GPart%d: start: %08lx size: %08lx\n",
+ part,pinfo[part].start,pinfo[part].size);
+ part++;
+
+ skip:
+ pptr += part_entry_size;
+ part_entries--;
+ }
+
+ is_gpt = 0; /* To break out of the loop */
+ }
disk_writer_unlock();
init = true;
@@ -174,6 +307,13 @@ int disk_mount(int drive)
int volume = get_free_volume();
+ if (volume < 0)
+ {
+ DEBUGF("No Free Volumes\n");
+ disk_writer_unlock();
+ return 0;
+ }
+
if (!disk_init(IF_MD(drive)))
{
disk_writer_unlock();
@@ -185,8 +325,7 @@ int disk_mount(int drive)
disk_sector_multiplier[IF_MD_DRV(drive)] = 1;
#endif
-
- /* try "superfloppy" mode */
+ /* try "superfloppy" mode */
DEBUGF("Trying to mount sector 0.\n");
if (!fat_mount(IF_MV(volume,) IF_MD(drive,) 0))
@@ -196,19 +335,21 @@ int disk_mount(int drive)
fat_get_bytes_per_sector(IF_MV(volume)) / SECTOR_SIZE;
#endif
mounted = 1;
- vol_drive[volume] = drive; /* remember the drive for this volume */
+ init_volume(&volumes[volume], drive, 0);
volume_onmount_internal(IF_MV(volume));
}
if (mounted == 0 && volume != -1) /* not a "superfloppy"? */
{
for (int i = CONFIG_DEFAULT_PARTNUM;
- volume != -1 && i < 4 && mounted < NUM_VOLUMES_PER_DRIVE;
+ volume != -1 && i < MAX_PARTITIONS_PER_DRIVE && mounted < NUM_VOLUMES_PER_DRIVE;
i++)
{
if (pinfo[i].type == 0 || pinfo[i].type == 5)
continue; /* skip free/extended partitions */
+ DEBUGF("Trying to mount partition %d.\n", i);
+
#ifdef MAX_LOG_SECTOR_SIZE
for (int j = 1; j <= (MAX_LOG_SECTOR_SIZE/SECTOR_SIZE); j <<= 1)
{
@@ -217,7 +358,7 @@ int disk_mount(int drive)
pinfo[i].start *= j;
pinfo[i].size *= j;
mounted++;
- vol_drive[volume] = drive; /* remember the drive for this volume */
+ init_volume(&volumes[volume], drive, i);
disk_sector_multiplier[drive] = j;
volume_onmount_internal(IF_MV(volume));
volume = get_free_volume(); /* prepare next entry */
@@ -228,7 +369,7 @@ int disk_mount(int drive)
if (!fat_mount(IF_MV(volume,) IF_MD(drive,) pinfo[i].start))
{
mounted++;
- vol_drive[volume] = drive; /* remember the drive for this volume */
+ init_volume(&volumes[volume], drive, i);
volume_onmount_internal(IF_MV(volume));
volume = get_free_volume(); /* prepare next entry */
}
@@ -250,26 +391,11 @@ int disk_mount_all(void)
volume_onunmount_internal(IF_MV(-1));
fat_init();
+ /* mark all volumes as free */
for (int i = 0; i < NUM_VOLUMES; i++)
- vol_drive[i] = -1; /* mark all as unassigned */
+ mark_free_volume(&volumes[i]);
-#if defined(HAVE_BOOTDATA) && !defined(SIMULATOR) && !defined(BOOTLOADER)
- unsigned int crc = 0;
- int boot_volume = 0;
- crc = crc_32(boot_data.payload, boot_data.length, 0xffffffff);
- if(crc == boot_data.crc)
- {
- boot_volume = boot_data.boot_volume; /* boot volume contained in uint8_t payload */
- }
- #ifdef HAVE_HOTSWAP
- if (storage_present(boot_volume))
- #endif
- mounted += disk_mount(boot_volume); /* mount boot volume first */
- for (int i = 0; i < NUM_DRIVES; i++)
- if (i != boot_volume)
-#else
for (int i = 0; i < NUM_DRIVES; i++)
-#endif
{
#ifdef HAVE_HOTSWAP
if (storage_present(i))
@@ -292,13 +418,13 @@ int disk_unmount(int drive)
for (int i = 0; i < NUM_VOLUMES; i++)
{
- if (vol_drive[i] == drive)
- { /* force releasing resources */
- vol_drive[i] = -1; /* mark unused */
-
+ struct volumeinfo *vi = &volumes[i];
+ /* unmount any volumes on the drive */
+ if (vi->drive == drive)
+ {
+ mark_free_volume(vi); /* FIXME: should do this after unmount? */
volume_onunmount_internal(IF_MV(i));
fat_unmount(IF_MV(i));
-
unmounted++;
}
}
@@ -397,6 +523,7 @@ enum volume_info_type
#if defined (HAVE_MULTIDRIVE) || defined (HAVE_DIRCACHE)
VP_DRIVE,
#endif
+ VP_PARTITION,
};
static int volume_properties(int volume, enum volume_info_type infotype)
@@ -407,22 +534,25 @@ static int volume_properties(int volume, enum volume_info_type infotype)
if (CHECK_VOL(volume))
{
- int vd = vol_drive[volume];
+ struct volumeinfo *vi = &volumes[volume];
switch (infotype)
{
#ifdef HAVE_HOTSWAP
case VP_REMOVABLE:
- res = storage_removable(vd) ? 1 : 0;
+ res = storage_removable(vi->drive) ? 1 : 0;
break;
case VP_PRESENT:
- res = storage_present(vd) ? 1 : 0;
+ res = storage_present(vi->drive) ? 1 : 0;
break;
#endif
#if defined(HAVE_MULTIDRIVE) || defined(HAVE_DIRCACHE)
case VP_DRIVE:
- res = vd;
+ res = vi->drive;
break;
#endif
+ case VP_PARTITION:
+ res = vi->partition;
+ break;
}
}
@@ -449,6 +579,11 @@ int volume_drive(int volume)
}
#endif /* HAVE_MULTIDRIVE */
+int volume_partition(int volume)
+{
+ return volume_properties(volume, VP_PARTITION);
+}
+
#ifdef HAVE_DIRCACHE
bool volume_ismounted(IF_MV_NONVOID(int volume))
{
@@ -456,4 +591,5 @@ bool volume_ismounted(IF_MV_NONVOID(int volume))
}
#endif /* HAVE_DIRCACHE */
+
#endif /* HAVE_HOTSWAP || HAVE_MULTIDRIVE || HAVE_DIRCACHE */
diff --git a/firmware/common/file.c b/firmware/common/file.c
index cb918c6eab..202410db81 100644
--- a/firmware/common/file.c
+++ b/firmware/common/file.c
@@ -28,9 +28,18 @@
#include "file.h"
#include "fileobj_mgr.h"
#include "disk_cache.h"
-#include "dircache_redirect.h"
+#include "rb_namespace.h"
#include "string-extra.h"
+/* Define LOGF_ENABLE to enable logf output in this file */
+//#define LOGF_ENABLE
+#ifdef LOGF_ENABLE
+#include "logf.h"
+#undef DEBUGF
+#define DEBUGF logf
+#endif
+
+
/**
* These functions provide a roughly POSIX-compatible file I/O API.
*/
@@ -494,6 +503,8 @@ static int open_internal_inner1(const char *path, int oflag,
return fildes;
file_error:
+ if (fildes >= 0)
+ close(fildes);
return rc;
}
@@ -598,8 +609,10 @@ static inline ssize_t readwrite_partial(struct filestr_desc *file,
static ssize_t readwrite(struct filestr_desc *file, void *buf, size_t nbyte,
bool write)
{
+#ifndef LOGF_ENABLE /* wipes out log before you can save it */
DEBUGF("readwrite(%p,%lx,%lu,%s)\n",
file, (long)buf, (unsigned long)nbyte, write ? "write" : "read");
+#endif
const file_size_t size = *file->sizep;
file_size_t filerem;
@@ -764,8 +777,9 @@ file_error:;
/* error or not, update the file offset and size if anything was
transferred */
file->offset += done;
+#ifndef LOGF_ENABLE /* wipes out log before you can save it */
DEBUGF("file offset: %ld\n", file->offset);
-
+#endif
/* adjust file size to length written */
if (write && file->offset > size)
*file->sizep = file->offset;
@@ -899,8 +913,9 @@ file_error:
/* move the read/write file offset */
off_t lseek(int fildes, off_t offset, int whence)
{
+#ifndef LOGF_ENABLE /* wipes out log before you can save it */
DEBUGF("lseek(fd=%d,ofs=%ld,wh=%d)\n", fildes, (long)offset, whence);
-
+#endif
struct filestr_desc * const file = GET_FILESTR(READER, fildes);
if (!file)
FILE_ERROR_RETURN(ERRNO, -1);
@@ -1123,9 +1138,44 @@ file_error:
return rc;
}
-
/** Extensions **/
+int modtime(const char *path, time_t modtime)
+{
+ DEBUGF("modtime(path=\"%s\",modtime=%d)\n", path, (int) modtime);
+
+ int rc, open1rc = -1;
+ struct filestr_base pathstr;
+ struct path_component_info pathinfo;
+
+ file_internal_lock_WRITER();
+
+ open1rc = open_stream_internal(path, FF_ANYTYPE | FF_PARENTINFO,
+ &pathstr, &pathinfo);
+ if (open1rc <= 0)
+ {
+ DEBUGF("Failed opening path: %d\n", open1rc);
+ if (open1rc == 0)
+ FILE_ERROR(ENOENT, -1);
+ else
+ FILE_ERROR(ERRNO, open1rc * 10 - 1);
+ }
+
+ rc = fat_modtime(&pathinfo.parentinfo.fatfile, pathstr.fatstr.fatfilep,
+ modtime);
+ if (rc < 0)
+ {
+ DEBUGF("I/O error during modtime: %d\n", rc);
+ FILE_ERROR(ERRNO, rc * 10 - 2);
+ }
+
+file_error:
+ if (open1rc >= 0)
+ close_stream_internal(&pathstr);
+ file_internal_unlock_WRITER();
+ return rc;
+}
+
/* get the binary size of a file (in bytes) */
off_t filesize(int fildes)
{
diff --git a/firmware/common/file_internal.c b/firmware/common/file_internal.c
index fe18f90056..a73d9beaa2 100644
--- a/firmware/common/file_internal.c
+++ b/firmware/common/file_internal.c
@@ -26,12 +26,18 @@
#include "pathfuncs.h"
#include "disk_cache.h"
#include "fileobj_mgr.h"
-#include "dir.h"
-#include "dircache_redirect.h"
-#include "dircache.h"
+#include "rb_namespace.h"
#include "string-extra.h"
#include "rbunicode.h"
+/* Define LOGF_ENABLE to enable logf output in this file */
+//#define LOGF_ENABLE
+#ifdef LOGF_ENABLE
+#include "logf.h"
+#undef DEBUGF
+#define DEBUGF logf
+#endif
+
/** Internal common filesystem service functions **/
/* for internal functions' scanning use to save quite a bit of stack space -
@@ -75,21 +81,23 @@ void file_cache_alloc(struct filestr_cache *cachep)
/* free resources attached to the cache */
void file_cache_free(struct filestr_cache *cachep)
{
- if (cachep && cachep->buffer)
+ if (cachep)
{
- dc_release_buffer(cachep->buffer);
- cachep->buffer = NULL;
+ if(cachep->buffer)
+ {
+ dc_release_buffer(cachep->buffer);
+ cachep->buffer = NULL;
+ }
+ file_cache_reset(cachep);
}
-
- file_cache_reset(cachep);
}
/** Stream base APIs **/
-static inline void filestr_clear(struct filestr_base *stream)
+static inline void filestr_clear(struct filestr_base *stream, unsigned int flags)
{
- stream->flags = 0;
+ stream->flags = flags;
stream->bindp = NULL;
#if 0
stream->mtx = NULL;
@@ -153,7 +161,7 @@ void filestr_discard_cache(struct filestr_base *stream)
/* Initialize the base descriptor */
void filestr_base_init(struct filestr_base *stream)
{
- filestr_clear(stream);
+ filestr_clear(stream, FD_VALID);
file_cache_init(&stream->cache);
stream->cachep = &stream->cache;
}
@@ -161,7 +169,7 @@ void filestr_base_init(struct filestr_base *stream)
/* free base descriptor resources */
void filestr_base_destroy(struct filestr_base *stream)
{
- filestr_clear(stream);
+ filestr_clear(stream, 0);
filestr_free_cache(stream);
}
@@ -219,6 +227,7 @@ void iso_decode_d_name(char *d_name)
return;
char shortname[13];
+ /* this only gets called in the case of DOS (8.3) filenames */
size_t len = strlcpy(shortname, d_name, sizeof (shortname));
/* This MUST be the default codepage thus not something that could be
loaded on call */
@@ -227,7 +236,7 @@ void iso_decode_d_name(char *d_name)
#ifdef HAVE_DIRCACHE
/* nullify all the fields of the struct dirent */
-void empty_dirent(struct dirent *entry)
+void empty_dirent(struct DIRENT *entry)
{
entry->d_name[0] = '\0';
entry->info.attr = 0;
@@ -249,7 +258,7 @@ void fill_dirinfo_native(struct dirinfo_native *dinp)
int uncached_readdir_dirent(struct filestr_base *stream,
struct dirscan_info *scanp,
- struct dirent *entry)
+ struct DIRENT *entry)
{
struct fat_direntry fatent;
int rc = fat_readdir(&stream->fatstr, &scanp->fatscan,
@@ -293,7 +302,7 @@ struct pathwalk_component
#define WALK_RC_NOT_FOUND 0 /* successfully not found (aid for file creation) */
#define WALK_RC_FOUND 1 /* found and opened */
-#define WALK_RC_FOUND_ROOT 2 /* found and opened sys/volume root */
+#define WALK_RC_FOUND_ROOT 2 /* found and opened sys root */
#define WALK_RC_CONT_AT_ROOT 3 /* continue at root level */
/* return another struct pathwalk_component from the pool, or NULL if the
@@ -397,10 +406,9 @@ static int walk_open_info(struct pathwalk *walkp,
/* make open official if not simply probing for presence - must do it here
or compp->info on stack will get destroyed before it was copied */
- if (!(callflags & FF_PROBE))
+ if (!(callflags & (FF_PROBE|FF_NOFS)))
fileop_onopen_internal(stream, &compp->info, callflags);
-
- return compp->nextp ? WALK_RC_FOUND : WALK_RC_FOUND_ROOT;
+ return compp->attr == ATTR_SYSTEM_ROOT ? WALK_RC_FOUND_ROOT : WALK_RC_FOUND;
}
/* check the component against the prefix test info */
@@ -507,6 +515,10 @@ walk_path(struct pathwalk *walkp, struct pathwalk_component *compp,
if (len > MAX_COMPNAME)
return -ENAMETOOLONG;
+ /* no filesystem is mounted here */
+ if (walkp->callflags & FF_NOFS)
+ return -ENOENT;
+
/* check for "." and ".." */
if (name[0] == '.')
{
@@ -575,7 +587,7 @@ int open_stream_internal(const char *path, unsigned int callflags,
callflags &= ~(FF_INFO | FF_PARENTINFO | FF_CHECKPREFIX);
/* This lets it be passed quietly to directory scanning */
- stream->flags = callflags & FF_MASK;
+ stream->flags |= callflags & FF_MASK;
struct pathwalk walk;
walk.path = path;
@@ -585,80 +597,36 @@ int open_stream_internal(const char *path, unsigned int callflags,
struct pathwalk_component *rootp = pathwalk_comp_alloc(NULL);
rootp->nextp = NULL;
- rootp->attr = ATTR_SYSTEM_ROOT;
-
-#ifdef HAVE_MULTIVOLUME
- int volume = 0, rootrc = WALK_RC_FOUND;
-#endif /* HAVE_MULTIVOLUME */
while (1)
{
- const char *pathptr = walk.path;
-
- #ifdef HAVE_MULTIVOLUME
- /* this seamlessly integrates secondary filesystems into the
- root namespace (e.g. "/<0>/../../<1>/../foo/." :<=> "/foo") */
- const char *p;
- volume = path_strip_volume(pathptr, &p, false);
- if (!CHECK_VOL(volume))
- {
- DEBUGF("No such device or address: %d\n", volume);
- FILE_ERROR(ENXIO, -2);
- }
-
- if (p == pathptr)
- {
- /* the root of this subpath is the system root */
- rootp->attr = ATTR_SYSTEM_ROOT;
- rootrc = WALK_RC_FOUND_ROOT;
- }
- else
- {
- /* this subpath specifies a mount point */
- rootp->attr = ATTR_MOUNT_POINT;
- rootrc = WALK_RC_FOUND;
- }
-
- walk.path = p;
- #endif /* HAVE_MULTIVOLUME */
-
- /* set name to start at last leading separator; names of volume
- specifiers will be returned as "/<fooN>" */
- rootp->name = GOBBLE_PATH_SEPCH(pathptr) - 1;
- rootp->length =
- IF_MV( rootrc == WALK_RC_FOUND ? p - rootp->name : ) 1;
+ rc = ns_parse_root(walk.path, &rootp->name, &rootp->length);
+ if (rc < 0)
+ break;
- rc = fat_open_rootdir(IF_MV(volume,) &rootp->info.fatfile);
+ rc = ns_open_root(IF_MV(rc,) &walk.callflags, &rootp->info, &rootp->attr);
if (rc < 0)
- {
- /* not mounted */
- DEBUGF("No such device or address: %d\n", IF_MV_VOL(volume));
- rc = -ENXIO;
break;
- }
- get_rootinfo_internal(&rootp->info);
+ walk.path = rootp->name + rootp->length;
+
rc = walk_path(&walk, rootp, stream);
if (rc != WALK_RC_CONT_AT_ROOT)
break;
}
- switch (rc)
+ if (rc >= 0)
{
- case WALK_RC_FOUND_ROOT:
- IF_MV( rc = rootrc; )
- case WALK_RC_NOT_FOUND:
- case WALK_RC_FOUND:
/* FF_PROBE leaves nothing for caller to clean up */
- if (callflags & FF_PROBE)
+ if (walk.callflags & FF_PROBE)
filestr_base_destroy(stream);
-
- break;
-
- default: /* utter, abject failure :`( */
+ }
+ else
+ {
+ /* utter, abject failure :`( */
DEBUGF("Open failed: rc=%d, errno=%d\n", rc, errno);
filestr_base_destroy(stream);
- FILE_ERROR(-rc, -3);
+ FILE_ERROR(-rc, -1);
}
file_error:
diff --git a/firmware/common/fileobj_mgr.c b/firmware/common/fileobj_mgr.c
index e34a460e10..da82681acc 100644
--- a/firmware/common/fileobj_mgr.c
+++ b/firmware/common/fileobj_mgr.c
@@ -20,12 +20,13 @@
****************************************************************************/
#include "config.h"
#include "system.h"
+#include <errno.h>
#include "debug.h"
#include "file.h"
#include "dir.h"
#include "disk_cache.h"
#include "fileobj_mgr.h"
-#include "dircache_redirect.h"
+#include "rb_namespace.h"
/**
* Manages file and directory streams on all volumes
@@ -34,8 +35,8 @@
*/
-/* there will always be enough of these for all user handles, thus these
- functions don't return failure codes */
+/* there will always be enough of these for all user handles, thus most of
+ these functions don't return failure codes */
#define MAX_FILEOBJS (MAX_OPEN_HANDLES + AUX_FILEOBJS)
/* describes the file as an image on the storage medium */
@@ -84,6 +85,14 @@ static struct ll_head busy_bindings[NUM_VOLUMES];
for (struct filestr_base *s = STREAM_##what(start); \
s; s = STREAM_NEXT(s))
+/* once a file/directory, always a file/directory; such a change
+ is a bug */
+#define CHECK_FO_DIRECTORY(callflags, fobp) \
+ if (((callflags) ^ (fobp)->flags) & FO_DIRECTORY) \
+ { \
+ DEBUGF("%s - FO_DIRECTORY flag does not match: %p %u\n", \
+ __func__, (fobp), (callflags)); \
+ }
/* syncs information for the stream's old and new parent directory if any are
currently opened */
@@ -96,6 +105,9 @@ static void fileobj_sync_parent(const struct file_base_info *infop[],
continue; /* not directory or removed can't be parent of anything */
struct filestr_base *parentstrp = STREAM_FIRST(fobp);
+ if (!parentstrp)
+ continue;
+
struct fat_file *parentfilep = &parentstrp->infop->fatfile;
for (int i = 0; i < count; i++)
@@ -111,8 +123,8 @@ static void fileobj_sync_parent(const struct file_base_info *infop[],
}
/* see if this file has open streams and return that fileobj_binding if so,
- else grab a new one from the free list; returns true if this stream is
- the only open one */
+ else grab a new one from the free list; returns true if this is new */
+
static bool binding_assign(const struct file_base_info *srcinfop,
struct fileobj_binding **fobpp)
{
@@ -123,7 +135,7 @@ static bool binding_assign(const struct file_base_info *srcinfop,
if (fat_file_is_same(&srcinfop->fatfile, &fobp->bind.info.fatfile))
{
- /* already has open streams */
+ /* already has open streams/mounts*/
*fobpp = fobp;
return false;
}
@@ -143,6 +155,22 @@ static void binding_add_to_free_list(struct fileobj_binding *fobp)
ll_insert_last(FREE_BINDINGS(), &fobp->bind.node);
}
+static void bind_source_info(const struct file_base_info *srcinfop,
+ struct fileobj_binding **fobpp)
+{
+ if (!binding_assign(srcinfop, fobpp))
+ return; /* already in use */
+ /* is new */
+ (*fobpp)->bind.info = *srcinfop;
+ fileobj_bind_file(&(*fobpp)->bind);
+}
+
+static void release_binding(struct fileobj_binding *fobp)
+{
+ fileobj_unbind_file(&fobp->bind);
+ binding_add_to_free_list(fobp);
+}
+
/** File and directory internal interface **/
void file_binding_insert_last(struct file_base_binding *bindp)
@@ -169,6 +197,33 @@ void file_binding_remove_next(struct file_base_binding *prevp,
}
#endif /* HAVE_DIRCACHE */
+/* mounts a file object as a target from elsewhere */
+bool fileobj_mount(const struct file_base_info *srcinfop,
+ unsigned int callflags,
+ struct file_base_binding **bindpp)
+{
+ struct fileobj_binding *fobp;
+ bind_source_info(srcinfop, &fobp);
+ CHECK_FO_DIRECTORY(callflags, fobp);
+ if (fobp->flags & FO_MOUNTTARGET)
+ return false; /* already mounted */
+ fobp->flags |= FDO_BUSY | FO_MOUNTTARGET |
+ (callflags & FO_DIRECTORY);
+ *bindpp = &fobp->bind;
+ return true;
+}
+/* unmounts the file object and frees it if now unusued */
+void fileobj_unmount(struct file_base_binding *bindp)
+{
+ struct fileobj_binding *fobp = (struct fileobj_binding *)bindp;
+ if (!(fobp->flags & FO_MOUNTTARGET))
+ return; /* not mounted */
+ if (STREAM_FIRST(fobp) == NULL)
+ release_binding(fobp); /* no longer in use */
+ else
+ fobp->flags &= ~FO_MOUNTTARGET;
+}
+
/* opens the file object for a new stream and sets up the caches;
* the stream must already be opened at the FS driver level and *stream
* initialized.
@@ -180,10 +235,13 @@ void fileobj_fileop_open(struct filestr_base *stream,
const struct file_base_info *srcinfop,
unsigned int callflags)
{
+ /* assign base file information */
struct fileobj_binding *fobp;
- bool first = binding_assign(srcinfop, &fobp);
+ bind_source_info(srcinfop, &fobp);
+ unsigned int foflags = fobp->flags;
/* add stream to this file's list */
+ bool first = STREAM_FIRST(fobp) == NULL;
ll_insert_last(&fobp->list, &stream->node);
/* initiate the new stream into the enclave */
@@ -197,27 +255,16 @@ void fileobj_fileop_open(struct filestr_base *stream,
if (first)
{
/* first stream for file */
- fobp->bind.info = *srcinfop;
- fobp->flags = FDO_BUSY | FO_SINGLE |
- (callflags & (FO_DIRECTORY|FO_TRUNC));
- fobp->writers = 0;
- fobp->size = 0;
-
- fileobj_bind_file(&fobp->bind);
+ fobp->flags = foflags | FDO_BUSY | FO_SINGLE |
+ (callflags & (FO_DIRECTORY|FO_TRUNC));
+ fobp->writers = 0;
+ fobp->size = 0;
}
else
{
/* additional stream for file */
- fobp->flags &= ~FO_SINGLE;
- fobp->flags |= callflags & FO_TRUNC;
-
- /* once a file/directory, always a file/directory; such a change
- is a bug */
- if ((callflags ^ fobp->flags) & FO_DIRECTORY)
- {
- DEBUGF("%s - FO_DIRECTORY flag does not match: %p %u\n",
- __func__, stream, callflags);
- }
+ fobp->flags = (foflags & ~FO_SINGLE) | (callflags & FO_TRUNC);
+ CHECK_FO_DIRECTORY(callflags, fobp);
}
if ((callflags & FD_WRITE) && ++fobp->writers == 1)
@@ -257,12 +304,14 @@ void fileobj_fileop_close(struct filestr_base *stream)
if (foflags & FO_SINGLE)
{
/* last stream for file; close everything */
- fileobj_unbind_file(&fobp->bind);
-
if (fobp->writers)
file_cache_free(&fobp->cache);
- binding_add_to_free_list(fobp);
+ /* binding must stay valid if something is mounted to here */
+ if (foflags & FO_MOUNTTARGET)
+ fobp->flags = foflags & (FDO_BUSY|FO_DIRECTORY|FO_MOUNTTARGET);
+ else
+ release_binding(fobp);
}
else
{
diff --git a/firmware/common/inflate.c b/firmware/common/inflate.c
new file mode 100644
index 0000000000..e39fe3a14e
--- /dev/null
+++ b/firmware/common/inflate.c
@@ -0,0 +1,793 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 by James Buren (libflate adaptations for RockBox)
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+/* Copyright 2021 Plan 9 Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "inflate.h"
+#include <stdbool.h>
+#include <string.h>
+#include "adler32.h"
+#include "crc32.h"
+#include "system.h"
+
+enum {
+ INFLATE_BUFFER_SIZE = 32768,
+ INFLATE_SYMBOL_MAX = 288,
+ INFLATE_OFFSET_MAX = 32,
+ INFLATE_CODELEN_MAX = 19,
+ INFLATE_HUFF_BITS = 17,
+ INFLATE_FLAT_BITS = 7,
+ INFLATE_SYMBOL_BITS = 7,
+ INFLATE_OFFSET_BITS = 6,
+ INFLATE_CODELEN_BITS = 6,
+};
+
+struct inflate_huff {
+ uint32_t max_bits;
+ uint32_t min_bits;
+ uint32_t flat_mask;
+ uint32_t flat[1 << INFLATE_FLAT_BITS];
+ uint32_t max_code[INFLATE_HUFF_BITS];
+ uint32_t last[INFLATE_HUFF_BITS];
+ uint32_t decode[INFLATE_SYMBOL_MAX];
+};
+
+struct inflate {
+ uint8_t in[INFLATE_BUFFER_SIZE];
+ uint8_t out[INFLATE_BUFFER_SIZE];
+ union {
+ struct {
+ uint8_t len[INFLATE_SYMBOL_MAX];
+ uint8_t off[INFLATE_OFFSET_MAX];
+ };
+ uint8_t combo[INFLATE_SYMBOL_MAX + INFLATE_OFFSET_MAX];
+ };
+ uint8_t clen[INFLATE_CODELEN_MAX];
+ struct inflate_huff lentab;
+ struct inflate_huff offtab;
+ struct inflate_huff clentab;
+ uint32_t bits[INFLATE_HUFF_BITS];
+ uint32_t codes[INFLATE_HUFF_BITS];
+};
+
+#define INFLATE_FILL(E) do { \
+ const uint32_t _size = read(is, sizeof(it->in), rctx); \
+ if (_size == 0) { \
+ rv = (E); \
+ goto bail; \
+ } \
+ ip = is; \
+ ie = is + _size; \
+} while (0)
+
+#define INFLATE_FLUSH(E) do { \
+ const uint32_t _size = (op - os); \
+ if (write(os, _size, wctx) != _size) { \
+ rv = (E); \
+ goto bail; \
+ } \
+ flushed = true; \
+ if (st == INFLATE_ZLIB) \
+ chksum = adler_32(os, _size, chksum); \
+ else if (st == INFLATE_GZIP) \
+ chksum = crc_32r(os, _size, chksum); \
+ op = os; \
+} while (0)
+
+#define INFLATE_GET_BYTE(E,C) do { \
+ if (ip == ie) \
+ INFLATE_FILL(E); \
+ (C) = ip[0]; \
+ ++ip; \
+} while (0)
+
+#define INFLATE_PUT_BYTE(E,B) do { \
+ if (op == oe) \
+ INFLATE_FLUSH(E); \
+ op[0] = (B); \
+ ++op; \
+} while (0)
+
+#define INFLATE_FILL_BITS(E,N) do { \
+ while ((N) > nbits) { \
+ uint8_t _byte; \
+ INFLATE_GET_BYTE(E, _byte); \
+ sreg |= (_byte << nbits); \
+ nbits += 8; \
+ } \
+} while (0)
+
+#define INFLATE_CONSUME_BITS(N) do { \
+ sreg >>= (N); \
+ nbits -= (N); \
+} while (0)
+
+#define INFLATE_EXTRACT_BITS(N,B) do { \
+ (B) = (sreg & ((1 << (N)) - 1)); \
+ INFLATE_CONSUME_BITS(N); \
+} while (0)
+
+#define INFLATE_CHECK_LENGTH(E,N) do { \
+ if ((N) > (ie - ip)) { \
+ rv = (E); \
+ goto bail; \
+ } \
+} while (0)
+
+#define INFLATE_REVERSE(C,B) ({ \
+ uint32_t _c = (C); \
+ _c <<= (16 - (B)); \
+ ((revtab[_c >> 8]) | (revtab[_c & 0xff] << 8)); \
+})
+
+#define INFLATE_DECODE(E,H) ({ \
+ __label__ _found; \
+ uint32_t _c = (H)->flat[sreg & (H)->flat_mask]; \
+ uint32_t _b = (_c & 0xff); \
+ uint32_t _code; \
+ if (_b == 0xff) { \
+ for (_b = (_c >> 8); _b <= (H)->max_bits; _b++) { \
+ _c = (revtab[sreg & 0xff] << 8); \
+ _c |= (revtab[(sreg >> 8) & 0xff]); \
+ _c >>= (16 - _b); \
+ if (_c <= (H)->max_code[_b]) { \
+ _code = (H)->decode[(H)->last[_b] - _c]; \
+ goto _found; \
+ } \
+ } \
+ rv = (E); \
+ goto bail; \
+ } \
+ _code = (_c >> 8); \
+_found: \
+ INFLATE_CONSUME_BITS(_b); \
+ _code; \
+})
+
+static int inflate_blocks(struct inflate* it, int st, inflate_reader read, void* rctx, inflate_writer write, void* wctx) {
+ int rv = 0;
+ uint8_t* is = it->in;
+ uint8_t* ip;
+ uint8_t* ie;
+ uint8_t* os = it->out;
+ uint8_t* op = os;
+ uint8_t* oe = os + sizeof(it->out);
+ bool flushed = false;
+ uint32_t chksum;
+ uint32_t nbits = 0;
+ uint32_t sreg = 0;
+ uint32_t i;
+ uint32_t j;
+ bool final;
+ uint8_t type;
+
+ INFLATE_FILL(-1);
+
+ if (st == INFLATE_ZLIB) {
+ uint16_t header;
+
+ INFLATE_CHECK_LENGTH(-2, 2);
+
+ header = (ip[0] << 8 | ip[1]);
+ ip += 2;
+
+ if ((header % 31) != 0) {
+ rv = -3;
+ goto bail;
+ }
+
+ if (((header & 0xf000) >> 12) > 7) {
+ rv = -4;
+ goto bail;
+ }
+
+ if (((header & 0x0f00) >> 8) != 8) {
+ rv = -5;
+ goto bail;
+ }
+
+ if (((header & 0x0020) >> 5) != 0) {
+ rv = -6;
+ goto bail;
+ }
+
+ chksum = 1;
+ } else if (st == INFLATE_GZIP) {
+ uint8_t flg;
+
+ INFLATE_CHECK_LENGTH(-7, 10);
+
+ if (ip[0] != 0x1f || ip[1] != 0x8b) {
+ rv = -8;
+ goto bail;
+ }
+
+ if (ip[2] != 8) {
+ rv = -9;
+ goto bail;
+ }
+
+ flg = ip[3];
+
+ if ((flg & 0xe0) != 0) {
+ rv = -10;
+ goto bail;
+ }
+
+ ip += 10;
+ chksum = 0xffffffff;
+
+ if ((flg & 0x04) != 0) {
+ uint16_t xlen;
+
+ INFLATE_CHECK_LENGTH(-11, 2);
+
+ xlen = (ip[0] | ip[1] << 8);
+ ip += 2;
+
+ while (xlen >= (ie - ip)) {
+ xlen -= (ie - ip);
+
+ if ((flg & 0x02) != 0)
+ chksum = crc_32r(is, ie - is, chksum);
+
+ INFLATE_FILL(-12);
+ }
+
+ ip += xlen;
+ }
+
+ if ((flg & 0x08) != 0) {
+ while (ip++[0] != '\0') {
+ if (ip == ie) {
+ if ((flg & 0x02) != 0)
+ chksum = crc_32r(is, ie - is, chksum);
+
+ INFLATE_FILL(-13);
+ }
+ }
+ }
+
+ if ((flg & 0x10) != 0) {
+ while (ip++[0] != '\0') {
+ if (ip == ie) {
+ if ((flg & 0x02) != 0)
+ chksum = crc_32r(is, ie - is, chksum);
+
+ INFLATE_FILL(-14);
+ }
+ }
+ }
+
+ if ((flg & 0x02) != 0) {
+ uint16_t crc16;
+
+ INFLATE_CHECK_LENGTH(-15, 2);
+
+ crc16 = (ip[0] | ip[1] << 8);
+ chksum = crc_32r(is, ip - is, chksum);
+ chksum &= 0xffff;
+ chksum ^= 0xffff;
+
+ if (crc16 != chksum) {
+ rv = -16;
+ goto bail;
+ }
+
+ ip += 2;
+ }
+
+ chksum = 0xffffffff;
+ } else {
+ chksum = 0;
+ }
+
+ do {
+ INFLATE_FILL_BITS(-17, 3);
+ final = (sreg & 0x01);
+ type = ((sreg & 0x06) >> 1);
+ INFLATE_CONSUME_BITS(3);
+
+ if (type == 0) {
+ uint8_t header[4];
+ uint32_t len;
+ uint32_t clen;
+
+ INFLATE_CONSUME_BITS(nbits & 0x07);
+
+ for (i = 0; i < 4; i++) {
+ if (nbits != 0)
+ INFLATE_EXTRACT_BITS(8, header[i]);
+ else
+ INFLATE_GET_BYTE(-18, header[i]);
+ }
+
+ len = (header[0] | (header[1] << 8));
+ clen = (header[2] | (header[3] << 8)) ^ 0xffff;
+
+ if (len != clen) {
+ rv = -19;
+ goto bail;
+ }
+
+ while (len != 0) {
+ if (ip == ie)
+ INFLATE_FILL(-20);
+
+ if (op == oe)
+ INFLATE_FLUSH(-21);
+
+ j = MIN(len, MIN((uint32_t) (ie - ip), (uint32_t) (oe - op)));
+ for (i = 0; i < j; i++)
+ op[i] = ip[i];
+
+ len -= j;
+ ip += j;
+ op += j;
+ }
+ } else if (type == 3) {
+ rv = -22;
+ goto bail;
+ } else {
+ static const uint8_t revtab[256] =
+ {
+ 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
+ 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
+ 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
+ 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
+ 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
+ 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
+ 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
+ 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
+ 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
+ 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
+ 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
+ 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
+ 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
+ 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
+ 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
+ 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
+ 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
+ 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
+ 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
+ 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
+ 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
+ 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
+ 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
+ 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
+ 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
+ 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
+ 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
+ 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
+ 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
+ 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
+ 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
+ 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
+ };
+ uint8_t* tab[3];
+ uint32_t tab_lens[3];
+ uint32_t tab_bits[3];
+ struct inflate_huff* tab_huffs[3] = { &it->lentab, &it->offtab, &it->clentab, };
+ uint32_t c;
+ uint32_t k;
+
+ if (type == 2) {
+ static const uint32_t min_tab_sizes[3] = { 257, 1, 4, };
+ static const uint32_t min_tab_bits[3] = { 5, 5, 4, };
+ static const uint32_t clen_order[INFLATE_CODELEN_MAX] = {
+ 16, 17, 18, 0, 8,
+ 7, 9, 6, 10, 5,
+ 11, 4, 12, 3, 13,
+ 2, 14, 1, 15,
+ };
+
+ INFLATE_FILL_BITS(-23, 14);
+ for (i = 0; i < 3; i++) {
+ INFLATE_EXTRACT_BITS(min_tab_bits[i], tab_lens[i]);
+ tab_lens[i] += min_tab_sizes[i];
+ }
+
+ tab[2] = it->clen;
+ for (i = 0; i < INFLATE_CODELEN_MAX; i++)
+ tab[2][i] = 0;
+ for (i = 0; i < tab_lens[2]; i++) {
+ INFLATE_FILL_BITS(-24, 3);
+ INFLATE_EXTRACT_BITS(3, tab[2][clen_order[i]]);
+ }
+
+ tab[0] = it->combo;
+ tab_bits[0] = INFLATE_SYMBOL_BITS;
+
+ tab[1] = tab[0] + tab_lens[0];
+ tab_bits[1] = INFLATE_OFFSET_BITS;
+
+ tab_lens[2] = INFLATE_CODELEN_MAX;
+ tab_bits[2] = INFLATE_CODELEN_BITS;
+ } else {
+ tab[0] = it->len;
+ for (i = 0; i < 144; i++)
+ tab[0][i] = 8;
+ for (; i < 256; i++)
+ tab[0][i] = 9;
+ for (; i < 280; i++)
+ tab[0][i] = 7;
+ for (; i < INFLATE_SYMBOL_MAX; i++)
+ tab[0][i] = 8;
+ tab_lens[0] = INFLATE_SYMBOL_MAX;
+ tab_bits[0] = INFLATE_FLAT_BITS;
+
+ tab[1] = it->off;
+ for (i = 0; i < INFLATE_OFFSET_MAX; i++)
+ tab[1][i] = 5;
+ tab_lens[1] = INFLATE_OFFSET_MAX;
+ tab_bits[1] = INFLATE_FLAT_BITS;
+ }
+
+ for(; type != 0xff; --type) {
+ const uint8_t* hb = tab[type];
+ uint32_t hb_len = tab_lens[type];
+ uint32_t flat_bits = tab_bits[type];
+ struct inflate_huff* h = tab_huffs[type];
+ uint32_t* bits = it->bits;
+ uint32_t* codes = it->codes;
+ uint32_t max_bits = 0;
+ uint32_t min_bits = INFLATE_HUFF_BITS + 1;
+ uint32_t min_code;
+ uint32_t b;
+ uint32_t code;
+ uint32_t fc;
+ uint32_t ec;
+
+ for (i = 0; i < INFLATE_HUFF_BITS; i++)
+ bits[i] = 0;
+
+ for (i = 0; i < hb_len; i++) {
+ b = hb[i];
+
+ if (b != 0) {
+ bits[b]++;
+
+ if (b < min_bits)
+ min_bits = b;
+
+ if (b > max_bits)
+ max_bits = b;
+ }
+ }
+
+ if (max_bits == 0) {
+ h->flat_mask = h->min_bits = h->max_bits = 0;
+ goto table_done;
+ }
+
+ h->max_bits = max_bits;
+
+ for (b = c = code = 0; b <= max_bits; b++) {
+ h->last[b] = c;
+ c += bits[b];
+
+ min_code = (code << 1);
+ codes[b] = min_code;
+ code = (min_code + bits[b]);
+
+ if (code > (1U << b)) {
+ rv = -25;
+ goto bail;
+ }
+
+ h->max_code[b] = (code - 1);
+ h->last[b] += (code - 1);
+ }
+
+ if (flat_bits > max_bits)
+ flat_bits = max_bits;
+
+ h->flat_mask = ((1 << flat_bits) - 1);
+
+ if (min_bits > flat_bits)
+ min_bits = flat_bits;
+
+ h->min_bits = min_bits;
+
+ for (i = 0, b = (1 << flat_bits); i < b; i++)
+ h->flat[i] = 0xffffffff;
+
+ for (b = max_bits; b > flat_bits; b--) {
+ code = h->max_code[b];
+
+ if (code == 0xffffffff)
+ break;
+
+ min_code = ((code + 1) - bits[b]);
+ min_code >>= (b - flat_bits);
+ code >>= (b - flat_bits);
+
+ for (; min_code <= code; min_code++)
+ h->flat[INFLATE_REVERSE(min_code, flat_bits)] = ((b << 8) | 0xff);
+ }
+
+ for (i = 0; i < hb_len; i++) {
+ b = hb[i];
+
+ if (b == 0)
+ continue;
+
+ c = codes[b]++;
+
+ if (b <= flat_bits) {
+ code = ((i << 8) | b);
+ ec = ((c + 1) << (flat_bits - b));
+
+ if (ec > (1U << flat_bits)) {
+ rv = -26;
+ goto bail;
+ }
+
+ for (fc = (c << (flat_bits - b)); fc < ec; fc++)
+ h->flat[INFLATE_REVERSE(fc, flat_bits)] = code;
+ }
+
+ if (b > min_bits) {
+ c = h->last[b] - c;
+
+ if (c >= hb_len) {
+ rv = -27;
+ goto bail;
+ }
+
+ h->decode[c] = i;
+ }
+ }
+
+ table_done:
+
+ if (type == 2) {
+ static const uint32_t bits[3] = { 2, 3, 7, };
+ static const uint32_t bases[3] = { 3, 3, 11, };
+
+ for (i = 0, j = tab_lens[0] + tab_lens[1]; i < j; ) {
+ uint32_t len;
+ uint8_t byte;
+
+ INFLATE_FILL_BITS(-28, h->max_bits);
+
+ c = INFLATE_DECODE(-29, h);
+
+ if (c < 16) {
+ tab[0][i++] = c;
+ continue;
+ }
+
+ c -= 16;
+
+ if (c == 0 && i == 0) {
+ rv = -30;
+ goto bail;
+ }
+
+ INFLATE_FILL_BITS(-31, bits[c]);
+ INFLATE_EXTRACT_BITS(bits[c], len);
+ len += bases[c];
+
+ if ((i + len) > j) {
+ rv = -32;
+ goto bail;
+ }
+
+ byte = ((c == 0) ? tab[0][i - 1] : 0);
+ for (k = 0; k < len; k++)
+ tab[0][i + k] = byte;
+
+ i += len;
+ }
+ }
+ }
+
+ while (1) {
+ static const uint32_t lenbase[INFLATE_SYMBOL_MAX - 257] = {
+ 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 13, 15, 17, 19, 23, 27, 31,
+ 35, 43, 51, 59, 67, 83, 99, 115,
+ 131, 163, 195, 227, 258, 0, 0,
+ };
+ static const uint32_t lenextra[INFLATE_SYMBOL_MAX - 257] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 2, 2, 2, 2,
+ 3, 3, 3, 3, 4, 4, 4, 4,
+ 5, 5, 5, 5, 0, 0, 0,
+ };
+ static const uint32_t offbase[INFLATE_OFFSET_MAX] = {
+ 1, 2, 3, 4, 5, 7, 9, 13,
+ 17, 25, 33, 49, 65, 97, 129, 193,
+ 257, 385, 513, 769, 1025, 1537, 2049, 3073,
+ 4097, 6145, 8193, 12289, 16385, 24577, 0, 0,
+ };
+ static const uint32_t offextra[INFLATE_OFFSET_MAX] = {
+ 0, 0, 0, 0, 1, 1, 2, 2,
+ 3, 3, 4, 4, 5, 5, 6, 6,
+ 7, 7, 8, 8, 9, 9, 10, 10,
+ 11, 11, 12, 12, 13, 13, 0, 0,
+ };
+ uint32_t len;
+ uint32_t off;
+ uint8_t* cp;
+
+ INFLATE_FILL_BITS(-33, tab_huffs[0]->max_bits);
+ c = INFLATE_DECODE(-34, tab_huffs[0]);
+
+ if (c < 256) {
+ INFLATE_PUT_BYTE(-35, c);
+ continue;
+ }
+
+ if (c == 256)
+ break;
+
+ if (c > 285) {
+ rv = -36;
+ goto bail;
+ }
+
+ c -= 257;
+ INFLATE_FILL_BITS(-37, lenextra[c]);
+ INFLATE_EXTRACT_BITS(lenextra[c], len);
+ len += lenbase[c];
+
+ INFLATE_FILL_BITS(-38, tab_huffs[1]->max_bits);
+ c = INFLATE_DECODE(-39, tab_huffs[1]);
+
+ if (c > 29) {
+ rv = -40;
+ goto bail;
+ }
+
+ INFLATE_FILL_BITS(-41, offextra[c]);
+ INFLATE_EXTRACT_BITS(offextra[c], off);
+ off += offbase[c];
+
+ cp = op - off;
+ if (cp < os) {
+ if (!flushed) {
+ rv = -42;
+ goto bail;
+ }
+
+ cp += sizeof(it->out);
+ }
+
+ while (len != 0) {
+ if (op == oe)
+ INFLATE_FLUSH(-43);
+
+ if (cp == oe)
+ cp = os;
+
+ j = MIN(len, MIN((uint32_t) (oe - op), (uint32_t) (oe - cp)));
+
+ for (i = 0; i < j; i++)
+ op[i] = cp[i];
+
+ op += j;
+ cp += j;
+ len -= j;
+ }
+ }
+ }
+ } while (!final);
+
+ INFLATE_FLUSH(-44);
+
+ if (st != INFLATE_RAW) {
+ uint8_t header[4];
+ uint32_t chksum2;
+
+ INFLATE_CONSUME_BITS(nbits & 0x07);
+
+ for (i = 0; i < 4; i++) {
+ if (nbits != 0)
+ INFLATE_EXTRACT_BITS(8, header[i]);
+ else
+ INFLATE_GET_BYTE(-45, header[i]);
+ }
+
+ if (st == INFLATE_ZLIB) {
+ chksum2 = ((header[0] << 24) | (header[1] << 16) | (header[2] << 8) | (header[3] << 0));
+
+ if (chksum != chksum2) {
+ rv = -46;
+ goto bail;
+ }
+ } else if (st == INFLATE_GZIP) {
+ chksum2 = ((header[3] << 24) | (header[2] << 16) | (header[1] << 8) | (header[0] << 0));
+
+ if (~chksum != chksum2) {
+ rv = -47;
+ goto bail;
+ }
+ }
+ }
+
+bail:
+ return rv;
+}
+
+const uint32_t inflate_size = sizeof(struct inflate);
+const uint32_t inflate_align = _Alignof(struct inflate);
+
+int inflate(struct inflate* it, int st, inflate_reader read, void* rctx, inflate_writer write, void* wctx) {
+ if (it == NULL || read == NULL || write == NULL || st < 0 || st > 2 || (((uintptr_t) it) & (_Alignof(struct inflate) - 1)) != 0)
+ return -48;
+
+ return inflate_blocks(it, st, read, rctx, write, wctx);
+}
+
+static uint32_t inflate_buffer_rw(struct inflate_bufferctx* c,
+ void* dst, const void* src, uint32_t block_size)
+{
+ size_t size_left = c->end - c->buf;
+ size_t copy_size = MIN((size_t)block_size, size_left);
+
+ memcpy(dst, src, copy_size);
+ c->buf += copy_size;
+
+ return copy_size;
+}
+
+uint32_t inflate_buffer_reader(void* block, uint32_t block_size, void* ctx)
+{
+ struct inflate_bufferctx* c = ctx;
+ return inflate_buffer_rw(c, block, c->buf, block_size);
+}
+
+uint32_t inflate_buffer_writer(const void* block, uint32_t block_size, void* ctx)
+{
+ struct inflate_bufferctx* c = ctx;
+ return inflate_buffer_rw(c, c->buf, block, block_size);
+}
+
+uint32_t inflate_getsize_writer(const void* block, uint32_t block_size, void* ctx)
+{
+ (void)block;
+
+ size_t* size = ctx;
+ *size += block_size;
+ return block_size;
+}
diff --git a/firmware/common/linked_list.c b/firmware/common/linked_list.c
index b8f2dd181c..30e4e6079a 100644
--- a/firmware/common/linked_list.c
+++ b/firmware/common/linked_list.c
@@ -46,15 +46,6 @@ static struct ll_node * ll_search_prev(struct ll_head *list,
}
/**
- * Initializes the singly-linked list
- */
-void ll_init(struct ll_head *list)
-{
- list->head = NULL;
- list->tail = NULL;
-}
-
-/**
* Adds a node to s singly-linked list using "insert next"
*/
void ll_insert_next(struct ll_head *list, struct ll_node *node,
@@ -142,16 +133,36 @@ void ll_remove(struct ll_head *list, struct ll_node *node)
/** (L)inked (L)ist (D)ouble **/
-/**
- * Initializes the doubly-linked list
- */
-void lld_init(struct lld_head *list)
+void lld_insert_next(struct lld_head *list, struct lld_node *node,
+ struct lld_node *newnode)
+{
+ struct lld_node **nodep = node != NULL ? &node->next : &list->head;
+ struct lld_node *next = *nodep;
+
+ newnode->next = next;
+ newnode->prev = node;
+ *nodep = newnode;
+
+ if (next == NULL)
+ list->tail = newnode;
+ else
+ next->prev = newnode;
+}
+
+void lld_insert_prev(struct lld_head *list, struct lld_node *node,
+ struct lld_node *newnode)
{
- list->head = NULL;
- list->tail = NULL;
+ struct lld_node **nodep = node != NULL ? &node->prev : &list->tail;
+ struct lld_node *prev = *nodep;
- /* tail could be stored in first item's prev pointer but this simplifies
- the routines and maintains the non-circularity */
+ newnode->next = node;
+ newnode->prev = prev;
+ *nodep = newnode;
+
+ if (prev == NULL)
+ list->head = newnode;
+ else
+ prev->next = newnode;
}
/**
@@ -238,14 +249,6 @@ static inline struct lldc_node * lldc_insert(struct lldc_head *list,
}
/**
- * Initializes the doubly-linked circular list
- */
-void lldc_init(struct lldc_head *list)
-{
- list->head = NULL;
-}
-
-/**
* Adds a node to a doubly-linked circular list using "insert first"
*/
void lldc_insert_first(struct lldc_head *list, struct lldc_node *node)
diff --git a/firmware/common/multiboot.c b/firmware/common/multiboot.c
new file mode 100644
index 0000000000..c2cedc102d
--- /dev/null
+++ b/firmware/common/multiboot.c
@@ -0,0 +1,113 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2017, 2020 by William Wilgus
+ *
+ * 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 "bootdata.h"
+#include "crc32.h"
+#include "loader_strerror.h"
+#include "file.h"
+#include <string.h>
+#include <stdio.h>
+
+/* Write bootdata into location in FIRMWARE marked by magic header
+ * Assumes buffer is already loaded with the firmware image
+ * We just need to find the location and write data into the
+ * payload region along with the crc for later verification and use.
+ * Returns payload len on success,
+ * On error returns EKEY_NOT_FOUND
+ */
+int write_bootdata(unsigned char* buf, int len, unsigned int boot_volume)
+{
+ struct boot_data_t bl_boot_data;
+ struct boot_data_t *fw_boot_data = NULL;
+ int search_len = MIN(len, BOOT_DATA_SEARCH_SIZE) - sizeof(struct boot_data_t);
+ int payload_len = EKEY_NOT_FOUND;
+
+ /* search for boot data header prior to search_len */
+ for(int i = 0; i < search_len; i++)
+ {
+ fw_boot_data = (struct boot_data_t*) &buf[i];
+ if (fw_boot_data->magic[0] != BOOT_DATA_MAGIC0 ||
+ fw_boot_data->magic[1] != BOOT_DATA_MAGIC1)
+ continue;
+
+ memset(&bl_boot_data.payload, 0, BOOT_DATA_PAYLOAD_SIZE);
+ bl_boot_data.boot_volume = boot_volume;
+
+ memset(fw_boot_data->payload, 0, fw_boot_data->length);
+ /* determine maximum bytes we can write to firmware
+ BOOT_DATA_PAYLOAD_SIZE is the size the bootloader expects */
+ payload_len = MIN(BOOT_DATA_PAYLOAD_SIZE, fw_boot_data->length);
+ fw_boot_data->length = payload_len;
+ /* copy data to FIRMWARE bootdata struct */
+ memcpy(fw_boot_data->payload, &bl_boot_data.payload, payload_len);
+ /* crc will be used within the firmware to check validity of bootdata */
+ fw_boot_data->crc = crc_32(fw_boot_data->payload, payload_len, 0xffffffff);
+ break;
+
+ }
+ return payload_len;
+}
+
+#ifdef HAVE_MULTIBOOT
+/* Check in root of this <volume> for rockbox_main.<playername>
+ * if this file empty or there is a single slash '/'
+ * buf = '<volume#>/<rootdir>/<firmware(name)>\0'
+ * If instead '/<*DIRECTORY*>' is supplied
+ * addpath will be set to this DIRECTORY buf =
+ * '/<volume#>/addpath/<rootdir>/<firmware(name)>\0'
+ * On error returns Negative number or 0
+ * On success returns bytes from snprintf
+ * and generated path will be placed in buf
+ * note: if supplied buffer is too small return will be
+ * the number of bytes that would have been written
+ */
+int get_redirect_dir(char* buf, int buffer_size, int volume,
+ const char* rootdir, const char* firmware)
+{
+ int fd;
+ size_t f_offset;
+ char add_path[MAX_PATH];
+ /* Check in root of volume for rockbox_main.<playername> redirect */
+ snprintf(add_path, sizeof(add_path), "/<%d>/"BOOT_REDIR, volume);
+ fd = open(add_path, O_RDONLY);
+ if (fd < 0)
+ return EFILE_NOT_FOUND;
+
+ /*clear add_path for re-use*/
+ memset(add_path, 0, sizeof(add_path));
+ f_offset = read(fd, add_path,sizeof(add_path));
+ close(fd);
+
+ for(size_t i = f_offset - 1; i < f_offset; i--)
+ {
+ /* strip control chars < SPACE or all if path doesn't start with '/' */
+ if (add_path[i] < 0x20 || add_path[0] != '/')
+ add_path[i] = '\0';
+ }
+ /* if '/add_path' is specified in rockbox_main.<playername>
+ path is /<vol#>/add_path/rootdir/firmwarename
+ if add_path is empty or '/' is missing from beginning
+ path is /<vol#>/rootdir/firmwarename
+ */
+ return snprintf(buf, buffer_size, "/<%d>%s/%s/%s", volume, add_path,
+ rootdir, firmware);
+}
+#endif
diff --git a/firmware/common/pathfuncs.c b/firmware/common/pathfuncs.c
index 1c48a54972..fa296cc2ed 100644
--- a/firmware/common/pathfuncs.c
+++ b/firmware/common/pathfuncs.c
@@ -23,9 +23,9 @@
#include "system.h"
#include "pathfuncs.h"
#include "string-extra.h"
+#include <stdio.h>
#ifdef HAVE_MULTIVOLUME
-#include <stdio.h>
#include "storage.h"
enum storage_name_dec_indexes
@@ -109,12 +109,12 @@ static const unsigned char storage_dec_indexes[STORAGE_NUM_TYPES+1] =
/* Returns on which volume this is and sets *nameptr to the portion of the
* path after the volume specifier, which could be the null if the path is
* just a volume root. If *nameptr > name, then a volume specifier was
- * found. If 'greedy' is 'true', then it all separators after the volume
- * specifier are consumed, if one was found.
+ * found. If 'greedy' is 'true', then all separators after the volume
+ * specifier are consumed.
*/
int path_strip_volume(const char *name, const char **nameptr, bool greedy)
{
- int volume = 0;
+ int volume = ROOT_VOLUME;
const char *t = name;
int c, v = 0;
@@ -123,9 +123,15 @@ int path_strip_volume(const char *name, const char **nameptr, bool greedy)
* digits within the brackets is parsed as the volume number and of
* those, only the last ones VOL_MUM_MAX allows.
*/
- c = *(t = GOBBLE_PATH_SEPCH(t)); /* skip all leading slashes */
+ t = GOBBLE_PATH_SEPCH(t); /* skip all leading slashes */
+ if (t == name)
+ {
+ volume = -1; /* relative path; don't know */
+ goto psv_out;
+ }
+ c = *t;
if (c != VOL_START_TOK) /* missing start token? no volume */
- goto volume0;
+ goto psv_out;
do
{
@@ -136,7 +142,7 @@ int path_strip_volume(const char *name, const char **nameptr, bool greedy)
break;
case '\0':
case PATH_SEPCH: /* no closing bracket; no volume */
- goto volume0;
+ goto psv_out;
default: /* something else; reset volume */
v = 0;
}
@@ -146,7 +152,7 @@ int path_strip_volume(const char *name, const char **nameptr, bool greedy)
if (!(c = *++t)) /* no more path and no '/' is ok */
;
else if (c != PATH_SEPCH) /* more path and no separator after end */
- goto volume0;
+ goto psv_out;
else if (greedy)
t = GOBBLE_PATH_SEPCH(++t); /* strip remaining separators */
@@ -155,21 +161,59 @@ int path_strip_volume(const char *name, const char **nameptr, bool greedy)
volume = v;
name = t;
-volume0:
+psv_out:
if (nameptr)
*nameptr = name;
return volume;
}
+/* Strip the last volume component in the path and return the remainder of
+ * the path in *nameptr. If 'greedy' is 'true', then all separators after
+ * the volume specifier are consumed.
+ */
+int path_strip_last_volume(const char *name, const char **nameptr, bool greedy)
+{
+ const char *p = name + strlen(name);
+
+ while (p > name)
+ {
+ /* skip the component */
+ while (p > name && p[-1] != PATH_SEPCH)
+ --p;
+
+ /* bail if we reached the beginning */
+ if (p <= name+1)
+ break;
+
+ /* point at the seprator */
+ --p;
+
+ /* try to strip the volume and return it if found */
+ int volume = path_strip_volume(p, nameptr, greedy);
+ if (volume != ROOT_VOLUME)
+ return volume;
+
+ /* skip any extra separators */
+ while (p > name && p[-1] == PATH_SEPCH)
+ --p;
+ }
+
+ /* return whatever is at the beginning of the path */
+ return path_strip_volume(name, nameptr, greedy);
+}
+
/* Returns the volume specifier decorated with the storage type name.
* Assumes the supplied buffer size is at least {VOL_MAX_LEN}+1.
*/
int get_volume_name(int volume, char *buffer)
{
- if (volume < 0)
+ if (volume < 0 || volume == ROOT_VOLUME)
{
- *buffer = '\0';
- return 0;
+ char *t = buffer;
+ if (volume == ROOT_VOLUME)
+ *t++ = PATH_ROOTCHR;
+ *t = '\0';
+ return t - buffer;
}
volume %= VOL_NUM_MAX; /* as path parser would have it */
@@ -182,8 +226,20 @@ int get_volume_name(int volume, char *buffer)
return snprintf(buffer, VOL_MAX_LEN + 1, "%c%s%d%c",
VOL_START_TOK, voldec, volume, VOL_END_TOK);
}
+
+/* Returns volume name formatted with the root. Assumes buffer size is at
+ * least {VOL_MAX_LEN}+2 */
+int make_volume_root(int volume, char *buffer)
+{
+ char *t = buffer;
+ if (volume >= 0 && volume != ROOT_VOLUME)
+ *t++ = PATH_ROOTCHR;
+ t += get_volume_name(volume, t);
+ return t - buffer;
+}
#endif /* HAVE_MULTIVOLUME */
+
/* Just like path_strip_volume() but strips a leading drive specifier and
* returns the drive number (A=0, B=1, etc.). -1 means no drive was found.
* If 'greedy' is 'true', all separators after the volume are consumed.
@@ -340,7 +396,62 @@ void path_correct_separators(char *dstpath, const char *path)
strcpy(dstp, p);
}
+/* Remove dot segments from the path
+ *
+ * 'path' and 'dstpath' may either be the same buffer or non-overlapping
+ */
+void path_remove_dot_segments (char *dstpath, const char *path)
+{
+ char *dstp = dstpath;
+ char *odstp = dstpath;
+ const char *p = path;
+
+ while (*p)
+ {
+ if (p[0] == '.' && p[1] == PATH_SEPCH)
+ p += 2;
+ else if (p[0] == '.' && p[1] == '.' && p[2] == PATH_SEPCH)
+ p += 3;
+ else if (p[0] == PATH_SEPCH && p[1] == '.' && p[2] == PATH_SEPCH)
+ p += 2;
+ else if (p[0] == PATH_SEPCH && p[1] == '.' && !p[2])
+ {
+ *dstp++ = PATH_SEPCH;
+ break;
+ }
+ else if (p[0] == PATH_SEPCH && p[1] == '.' &&
+ p[2] == '.' && p[3] == PATH_SEPCH)
+ {
+ dstp = odstp;
+ p += 3;
+ }
+ else if (p[0] == PATH_SEPCH && p[1] == '.' && p[2] == '.' && !p[3])
+ {
+ dstp = odstp;
+ *dstp++ = PATH_SEPCH;
+ break;
+ }
+ else if (p[0] == '.' && !p[1])
+ break;
+ else if (p[0] == '.' && p[1] == '.' && !p[2])
+ break;
+ else
+ {
+ odstp = dstp;
+ if (p[0] == PATH_SEPCH)
+ *dstp++ = *p++;
+ while (p[0] && p[0] != PATH_SEPCH)
+ *dstp++ = *p++;
+ }
+ }
+ *dstp = 0;
+}
+
/* Appends one path to another, adding separators between components if needed.
+ * basepath_max can be used to truncate the basepath if desired
+ * NOTE: basepath is truncated after copying to the buffer so there must be enough
+ * free space for the entirety of the basepath even if the resulting string would fit
+ *
* Return value and behavior is otherwise as strlcpy so that truncation may be
* detected.
*
@@ -348,9 +459,11 @@ void path_correct_separators(char *dstpath, const char *path)
* PA_SEP_HARD adds a separator even if the base path is empty
* PA_SEP_SOFT adds a separator only if the base path is not empty
*/
-size_t path_append(char *buf, const char *basepath,
+size_t path_append_ex(char *buf, const char *basepath, size_t basepath_max,
const char *component, size_t bufsize)
{
+ size_t len;
+ bool separate = false;
const char *base = basepath && basepath[0] ? basepath : buf;
if (!base)
return bufsize; /* won't work to get lengths from buf */
@@ -363,15 +476,25 @@ size_t path_append(char *buf, const char *basepath,
/* 'component' is absolute; replace all */
basepath = component;
component = "";
+ basepath_max = -1u;
}
/* if basepath is not null or empty, buffer contents are replaced,
otherwise buf contains the base path */
- size_t len = base == buf ? strlen(buf) : strlcpy(buf, basepath, bufsize);
- bool separate = false;
+ if (base == buf)
+ len = strlen(buf);
+ else
+ {
+ len = strlcpy(buf, basepath, bufsize);
+ if (basepath_max < len)
+ {
+ len = basepath_max;
+ buf[basepath_max] = '\0';
+ }
+ }
- if (!basepath || !component)
+ if (!basepath || !component || basepath_max == 0)
separate = !len || base[len-1] != PATH_SEPCH;
else if (component[0])
separate = len && base[len-1] != PATH_SEPCH;
@@ -389,6 +512,12 @@ size_t path_append(char *buf, const char *basepath,
return len + strlcpy(buf, component ?: "", bufsize);
}
+
+size_t path_append(char *buf, const char *basepath,
+ const char *component, size_t bufsize)
+{
+ return path_append_ex(buf, basepath, -1u, component, bufsize);
+}
/* Returns the location and length of the next path component, consuming the
* input in the process.
*
diff --git a/firmware/common/rb-loader.c b/firmware/common/rb-loader.c
index 300ba55401..430ed6ec7b 100644
--- a/firmware/common/rb-loader.c
+++ b/firmware/common/rb-loader.c
@@ -26,96 +26,9 @@
#include "loader_strerror.h"
#include "checksum.h"
-#if defined(HAVE_BOOTDATA)
-#include "bootdata.h"
-#include "crc32.h"
-
-/* Write bootdata into location in FIRMWARE marked by magic header
- * Assumes buffer is already loaded with the firmware image
- * We just need to find the location and write data into the
- * payload region along with the crc for later verification and use.
- * Returns payload len on success,
- * On error returns EKEY_NOT_FOUND
- */
-int write_bootdata(unsigned char* buf, int len, unsigned int boot_volume)
-{
- struct boot_data_t bl_boot_data;
- struct boot_data_t *fw_boot_data = NULL;
- int search_len = MIN(len, BOOT_DATA_SEARCH_SIZE) - sizeof(struct boot_data_t);
- int payload_len = EKEY_NOT_FOUND;
-
- /* search for boot data header prior to search_len */
- for(int i = 0;i < search_len;i++)
- {
- fw_boot_data = (struct boot_data_t*) &buf[i];
- if (fw_boot_data->magic[0] != BOOT_DATA_MAGIC0 ||
- fw_boot_data->magic[1] != BOOT_DATA_MAGIC1)
- continue;
-
- memset(&bl_boot_data.payload, 0, BOOT_DATA_PAYLOAD_SIZE);
- bl_boot_data.boot_volume = boot_volume;
-
- memset(fw_boot_data->payload, 0, fw_boot_data->length);
- /* determine maximum bytes we can write to firmware
- BOOT_DATA_PAYLOAD_SIZE is the size the bootloader expects */
- payload_len = MIN(BOOT_DATA_PAYLOAD_SIZE, fw_boot_data->length);
- fw_boot_data->length = payload_len;
- /* copy data to FIRMWARE bootdata struct */
- memcpy(fw_boot_data->payload, &bl_boot_data.payload, payload_len);
- /* crc will be used within the firmware to check validity of bootdata */
- fw_boot_data->crc = crc_32(fw_boot_data->payload, payload_len, 0xffffffff);
- break;
-
- }
- return payload_len;
-}
-#endif /* HAVE_BOOTDATA */
-
-#ifdef HAVE_MULTIBOOT /* defined by config.h */
-/* Check in root of this <volume> for rockbox_main.<playername>
- * if this file empty or there is a single slash '/'
- * buf = '<volume#>/<rootdir>/<firmware(name)>\0'
- * If instead '/<*DIRECTORY*>' is supplied
- * addpath will be set to this DIRECTORY buf =
- * '/<volume#>/addpath/<rootdir>/<firmware(name)>\0'
- * On error returns Negative number or 0
- * On success returns bytes from snprintf
- * and generated path will be placed in buf
- * note: if supplied buffer is too small return will be
- * the number of bytes that would have been written
- */
-int get_redirect_dir(char* buf, int buffer_size, int volume,
- const char* rootdir, const char* firmware)
-{
- int fd;
- int f_offset;
- char add_path[MAX_PATH];
- /* Check in root of volume for rockbox_main.<playername> redirect */
- snprintf(add_path, sizeof(add_path), "/<%d>/"BOOT_REDIR, volume);
- fd = open(add_path, O_RDONLY);
- if (fd < 0)
- return EFILE_NOT_FOUND;
-
- /*clear add_path for re-use*/
- memset(add_path, 0, sizeof(add_path));
- f_offset = read(fd, add_path,sizeof(add_path));
- close(fd);
-
- for(int i = f_offset - 1;i > 0; i--)
- {
- /* strip control chars < SPACE or all if path doesn't start with '/' */
- if (add_path[i] < 0x20 || add_path[0] != '/')
- add_path[i] = '\0';
- }
- /* if '/add_path' is specified in rockbox_main.<playername>
- path is /<vol#>/add_path/rootdir/firmwarename
- if add_path is empty or '/' is missing from beginning
- path is /<vol#>/rootdir/firmwarename
- */
- return snprintf(buf, buffer_size, "/<%d>%s/%s/%s", volume, add_path,
- rootdir, firmware);
-}
-#endif /* HAVE_MULTIBOOT */
+#if defined(HAVE_BOOTDATA) || defined(HAVE_MULTIBOOT)
+#include "multiboot.h"
+#endif
/* loads a firmware file from supplied filename
* file opened, checks firmware size and checksum
@@ -193,7 +106,7 @@ int load_firmware(unsigned char* buf, const char* firmware, int buffer_size)
* 0 is the default boot volume, it is not checked here
* if found <volume>/rockbox_main.<playername> and firmware
* has a bootdata region this firmware will be loaded */
- for (unsigned int i = NUM_VOLUMES - 1; i > 0 && ret < 0; i--)
+ for (int i = NUM_VOLUMES - 1; i >= MULTIBOOT_MIN_VOLUME && ret < 0; i--)
{
if (get_redirect_dir(filename, sizeof(filename), i,
BOOTDIR, firmware) > 0)
diff --git a/firmware/common/rb_namespace.c b/firmware/common/rb_namespace.c
new file mode 100644
index 0000000000..85f647c474
--- /dev/null
+++ b/firmware/common/rb_namespace.c
@@ -0,0 +1,383 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2017 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 "config.h"
+#include <errno.h>
+#include "fileobj_mgr.h"
+#include "rb_namespace.h"
+#include "file_internal.h"
+#include <stdio.h> /*snprintf*/
+
+/* Define LOGF_ENABLE to enable logf output in this file */
+//#define LOGF_ENABLE
+#include "logf.h"
+
+#if !defined(HAVE_MULTIVOLUME) && defined(LOGF_ENABLE)
+ int volume = 0;
+#endif
+
+
+#define ROOT_CONTENTS_INDEX (NUM_VOLUMES)
+#define NUM_ROOT_ITEMS (NUM_VOLUMES+1)
+
+static uint8_t root_entry_flags[NUM_VOLUMES+1];
+static struct file_base_binding *root_bindp;
+
+static inline unsigned int get_root_item_state(int item)
+{
+ return root_entry_flags[item];
+}
+
+static inline void set_root_item_state(int item, unsigned int state)
+{
+ root_entry_flags[item] = state;
+}
+
+static void get_mount_point_entry(IF_MV(int volume,) struct DIRENT *entry)
+{
+#ifdef HAVE_MULTIVOLUME
+ get_volume_name(volume, entry->d_name);
+#else /* */
+ strcpy(entry->d_name, PATH_ROOTSTR);
+#endif /* HAVE_MULTIVOLUME */
+#if defined(_FILESYSTEM_NATIVE_H_)
+ entry->info.attr = ATTR_MOUNT_POINT;
+ entry->info.size = 0;
+ entry->info.wrtdate = 0;
+ entry->info.wrttime = 0;
+#endif /* is dirinfo_native */
+ logf("%s: vol:%d, %s", __func__, volume, entry->d_name);
+}
+
+/* unmount the directory that enumerates into the root namespace */
+static void unmount_item(int item)
+{
+ unsigned int state = get_root_item_state(item);
+ logf("%s: state: %u", __func__, state);
+ if (!state)
+ return;
+
+ if (state & NSITEM_CONTENTS)
+ {
+ fileobj_unmount(root_bindp);
+ root_bindp = NULL;
+ }
+
+ set_root_item_state(item, 0);
+}
+
+static char *root_realpath_internal(void)
+{
+ static char root_realpath[ROOT_MAX_REALPATH];
+ return root_realpath;
+}
+const char* root_get_realpath(void)
+{
+ return root_realpath_internal();
+}
+
+/* mount the directory that enumerates into the root namespace */
+int root_mount_path(const char *path, unsigned int flags)
+{
+ const char *folder = NULL; /* is a folder enumerated in the root? */
+#ifdef HAVE_MULTIVOLUME
+ int volume = path_strip_volume(path, &folder, false);
+ if (volume == ROOT_VOLUME)
+ return -EINVAL;
+ if (!CHECK_VOL(volume))
+ return -ENOENT;
+ char volname[VOL_MAX_LEN+2];
+ make_volume_root(volume, volname);
+#else
+ const char *volname = PATH_ROOTSTR;
+ if (!path_is_absolute(path))
+ {
+ logf("Path not absolute %s", path);
+ return -ENOENT;
+ }
+ path_dirname(path, &folder);
+#endif /* HAVE_MULTIVOLUME */
+ bool contents = flags & NSITEM_CONTENTS;
+ int item = IF_MV_VOL(volume);
+ unsigned int state = get_root_item_state(item);
+ logf("%s: item:%d, st:%u, %s", __func__, item, state, path);
+ if (contents && state) /* volume must be mounted to enumerate into the root namespace */
+ {
+ if (get_root_item_state(ROOT_CONTENTS_INDEX))
+ return -EBUSY; /* error something is already enumerated */
+ /* cache information about the target */
+ struct filestr_base stream;
+ struct path_component_info compinfo;
+ int e = errno;
+ int rc = open_stream_internal(path, FF_DIR | FF_PROBE | FF_INFO |
+ FF_DEVPATH, &stream, &compinfo);
+ if (rc <= 0)
+ {
+ rc = rc ? -errno : -ENOENT;
+ errno = e;
+ return rc;
+ }
+ if (!fileobj_mount(&compinfo.info, FO_DIRECTORY, &root_bindp))
+ return -EBUSY;
+ int root_state = NSITEM_MOUNTED | (flags & (NSITEM_HIDDEN|NSITEM_CONTENTS));
+ set_root_item_state(ROOT_CONTENTS_INDEX, root_state);
+ flags |= state; /* preserve the state of the mounted volume */
+ if (!folder)
+ {
+ folder = "";
+ }
+ else
+ {
+ /*if a folder has been enumerated don't mark the whole volume */
+ if (folder[0] != '\0' && folder[1] != '\0')
+ flags &= ~NSITEM_CONTENTS;
+
+ }
+ snprintf(root_realpath_internal(), ROOT_MAX_REALPATH,"%s%s", volname, folder);
+ }
+ else if (state) /* error volume already mounted */
+ return -EBUSY;
+ state = NSITEM_MOUNTED | (flags & (NSITEM_HIDDEN|NSITEM_CONTENTS));
+ set_root_item_state(item, state);
+ return 0;
+}
+
+/* check if volume in path is mounted in the root namespace */
+bool ns_volume_is_visible(IF_MV_NONVOID(int volume))
+{
+ int item = IF_MV_VOL(volume);
+ if ((item == ROOT_VOLUME) || !CHECK_VOL(item))
+ return false;
+ unsigned int state = get_root_item_state(item);
+ return state && (((state & NSITEM_HIDDEN) == 0) || (state & NSITEM_CONTENTS));
+}
+
+/* inform root that an entire volume is being unmounted */
+void root_unmount_volume(IF_MV_NONVOID(int volume))
+{
+ logf("%s: vol: %d", __func__, volume);
+ FOR_EACH_VOLUME(volume, item)
+ {
+ #ifdef HAVE_MULTIVOLUME
+ uint32_t state = get_root_item_state(item);
+ if (state && (volume < 0 || item == volume))
+ #endif /* HAVE_MULTIVOLUME */
+ unmount_item(item);
+ }
+
+ /* if the volume unmounted contains the root directory contents then
+ the contents must also be unmounted */
+#ifdef HAVE_MULTIVOLUME
+ uint32_t state = get_root_item_state(ROOT_CONTENTS_INDEX);
+ if (state && (volume < 0 || BASEINFO_VOL(&root_bindp->info) == volume))
+#endif
+ {
+ unmount_item(ROOT_CONTENTS_INDEX);
+ root_realpath_internal()[0] = '\0';
+ }
+}
+
+/* parse the root part of a path */
+int ns_parse_root(const char *path, const char **pathp, uint16_t *lenp)
+{
+ logf("%s: path: %s", __func__, path);
+ int volume = ROOT_VOLUME;
+
+#ifdef HAVE_MULTIVOLUME
+ /* this seamlessly integrates secondary filesystems into the
+ root namespace (e.g. "/<0>/../../<1>/../foo/." :<=> "/foo") */
+ const char *p;
+ volume = path_strip_volume(path, &p, false);
+ if (volume != ROOT_VOLUME && !CHECK_VOL(volume))
+ {
+ logf("vol: %d is not root", volume);
+ return -ENOENT;
+ }
+#endif /* HAVE_MULTIVOLUME */
+
+ /* set name to start at last leading separator; name of root will
+ * be returned as "/", volume specifiers as "/<fooN>" */
+ *pathp = GOBBLE_PATH_SEPCH(path) - 1;
+ *lenp = IF_MV( volume < NUM_VOLUMES ? p - *pathp : ) 1;
+#ifdef LOGF_ENABLE
+ if (volume == INT_MAX)
+ logf("vol: ROOT(%d) %s", volume, *pathp);
+ else
+ logf("vol: %d %s", volume, *pathp);
+#endif
+#ifdef HAVE_MULTIVOLUME
+ if (*lenp > MAX_COMPNAME+1)
+ {
+ logf("%s: path too long %s", __func__, path);
+ return -ENAMETOOLONG;
+ }
+#endif
+#ifdef LOGF_ENABLE
+ if (volume == INT_MAX)
+ logf("%s: vol: ROOT(%d) path: %s", __func__, volume, path);
+ else
+ logf("%s: vol: %d path: %s", __func__, volume, path);
+#endif
+ return volume;
+}
+
+/* open one of the items in the root */
+int ns_open_root(IF_MV(int volume,) unsigned int *callflagsp,
+ struct file_base_info *infop, uint16_t *attrp)
+{
+ unsigned int callflags = *callflagsp;
+ bool devpath = !!(callflags & FF_DEVPATH);
+#ifdef HAVE_MULTIVOLUME
+ bool sysroot = volume == ROOT_VOLUME;
+ if (devpath && sysroot)
+ return -ENOENT; /* devpath needs volume spec */
+#else
+ bool sysroot = !devpath; /* always sysroot unless devpath */
+#endif
+
+ int item = sysroot ? ROOT_CONTENTS_INDEX : IF_MV_VOL(volume);
+ unsigned int state = get_root_item_state(item);
+ logf("%s: Vol:%d St:%d", __func__, item, state);
+ if (sysroot)
+ {
+ *attrp = ATTR_SYSTEM_ROOT;
+
+ if (state)
+ *infop = root_bindp->info;
+ else
+ {
+ logf("%s: SysRoot Vol:%d St:%d NOT mounted", __func__, item, state);
+ *callflagsp = callflags | FF_NOFS; /* contents not mounted */
+ }
+ }
+ else
+ {
+ *attrp = ATTR_MOUNT_POINT;
+
+ if (!devpath && !state)
+ return -ENOENT; /* regular open requires having been mounted */
+#if CONFIG_PLATFORM & PLATFORM_NATIVE
+ if (fat_open_rootdir(IF_MV(volume,) &infop->fatfile) < 0)
+ {
+ logf("%s: DevPath Vol:%d St:%d NOT mounted", __func__, item, state);
+ return -ENOENT; /* not mounted */
+ }
+#endif
+ get_rootinfo_internal(infop);
+ }
+
+ return 0;
+}
+
+/* read root directory entries */
+int root_readdir_dirent(struct filestr_base *stream,
+ struct ns_scan_info *scanp, struct DIRENT *entry)
+{
+ int rc = 0;
+
+ int item = scanp->item;
+ logf("%s: item: %d", __func__, item);
+
+ /* skip any not-mounted or hidden items */
+ unsigned int state;
+ while (1)
+ {
+ if (item >= NUM_ROOT_ITEMS)
+ goto file_eod;
+
+ state = get_root_item_state(item);
+ if ((state & (NSITEM_MOUNTED|NSITEM_HIDDEN)) == NSITEM_MOUNTED)
+ {
+#if 1 /* hide the volume enumerated into the root namespace */
+ if (item == ROOT_CONTENTS_INDEX || (state & NSITEM_CONTENTS) == 0)
+ {
+ logf("Found mounted item: %d %s", item, entry->d_name);
+ break;
+ }
+#endif
+ }
+
+ item++;
+ }
+
+ if (item == ROOT_CONTENTS_INDEX)
+ {
+ rc = readdir_dirent(stream, &scanp->scan, entry);
+ if (rc < 0)
+ FILE_ERROR(ERRNO, rc * 10 - 1);
+
+ if (rc == 0)
+ {
+ logf("Found root item: %d %s", item, entry->d_name);
+ item++;
+ }
+ }
+ else
+ {
+ get_mount_point_entry(IF_MV(item,) entry);
+ item++;
+ rc = 1;
+ logf("Found mp item:%d %s", item, entry->d_name);
+ }
+
+ scanp->item = item;
+
+file_eod:
+#ifdef HAVE_DIRCACHE
+ if (rc == 0)
+ empty_dirent(entry);
+#endif
+file_error:
+ logf("%s: status: %d", __func__, rc);
+ return rc;
+}
+
+/* opens a stream to enumerate items in a namespace container */
+int ns_open_stream(const char *path, unsigned int callflags,
+ struct filestr_base *stream, struct ns_scan_info *scanp)
+{
+ logf("%s: path: %s", __func__, path);
+ /* stream still needs synchronization even if we don't have a stream */
+ static struct mutex no_contents_mtx SHAREDBSS_ATTR;
+
+ int rc = open_stream_internal(path, callflags, stream, NULL);
+ if (rc < 0)
+ FILE_ERROR(ERRNO, rc * 10 - 1);
+
+ scanp->item = rc > 1 ? 0 : -1;
+
+ if (stream->flags & FDO_BUSY)
+ {
+ /* root contents are mounted */
+ fat_rewind(&stream->fatstr);
+ }
+ else
+ {
+ /* root contents not mounted */
+ mutex_init(&no_contents_mtx);
+ stream->mtx = &no_contents_mtx;
+ }
+
+ ns_dirscan_rewind(scanp);
+
+ rc = 0;
+file_error:
+ return rc;
+}
diff --git a/firmware/common/rectangle.c b/firmware/common/rectangle.c
new file mode 100644
index 0000000000..3ce09f5145
--- /dev/null
+++ b/firmware/common/rectangle.c
@@ -0,0 +1,141 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2022 Aidan MacDonald
+ *
+ * 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 "rectangle.h"
+#include "system.h"
+
+bool rect_contains(const struct rectangle *ra, const struct rectangle *rb)
+{
+ return ra->x <= rb->x && rb->x + rb->w < ra->x + ra->w &&
+ ra->y <= rb->y && rb->y + rb->h < ra->y + ra->h;
+}
+
+bool rect_overlap(const struct rectangle *ra, const struct rectangle *rb)
+{
+ return ra->x + ra->w > rb->x && rb->x + rb->w > ra->x &&
+ ra->y + ra->h > rb->y && rb->y + rb->h > ra->y;
+}
+
+bool rect_intersect(const struct rectangle *ra, const struct rectangle *rb,
+ struct rectangle *r_out)
+{
+ if (!rect_valid(ra) || !rect_valid(rb))
+ return false;
+
+ int x = MAX(ra->x, rb->x);
+ int y = MAX(ra->y, rb->y);
+ int w = MIN(ra->x + ra->w, rb->x + rb->w) - x;
+ int h = MIN(ra->y + ra->h, rb->y + rb->h) - y;
+
+ r_out->x = x;
+ r_out->y = y;
+ r_out->w = w;
+ r_out->h = h;
+
+ return w > 0 && h > 0;
+}
+
+void rect_union(const struct rectangle *ra, const struct rectangle *rb,
+ struct rectangle *r_out)
+{
+ if (!rect_valid(ra)) {
+ *r_out = *rb;
+ } else if (!rect_valid(rb)) {
+ *r_out = *ra;
+ } else {
+ int x = MIN(ra->x, rb->x);
+ int y = MIN(ra->y, rb->y);
+ int w = MAX(ra->x + ra->w, rb->x + rb->w) - x;
+ int h = MAX(ra->y + ra->h, rb->y + rb->h) - y;
+
+ r_out->x = x;
+ r_out->y = y;
+ r_out->w = w;
+ r_out->h = h;
+ }
+}
+
+int rect_difference(const struct rectangle *rect,
+ const struct rectangle *rsub,
+ struct rectangle rects_out[4])
+{
+ if (!rect_valid(rect) || !rect_valid(rsub)) {
+ rects_out[0] = *rect;
+ return 1;
+ }
+
+ int x0 = MAX(rect->x, rsub->x);
+ int y0 = MAX(rect->y, rsub->y);
+ int x1 = MIN(rect->x + rect->w, rsub->x + rsub->w);
+ int y1 = MIN(rect->y + rect->h, rsub->y + rsub->h);
+
+ /* no intersection */
+ if (x1 - x0 <= 0 || y1 - y0 <= 0) {
+ rects_out[0] = *rect;
+ return 1;
+ }
+
+ /* rect
+ * +-------------+
+ * | . 2 . |
+ * | +-----+ |
+ * | 0 |rsub | 1 |
+ * | +-----+ |
+ * | . 3 . |
+ * +-------------+
+ */
+
+ int n = 0;
+
+ if (rect->x < x0) {
+ rects_out[n].x = rect->x;
+ rects_out[n].y = rect->y;
+ rects_out[n].w = x0 - rect->x;
+ rects_out[n].h = rect->h;
+ n++;
+ }
+
+ if (x1 < rect->x + rect->w) {
+ rects_out[n].x = x1;
+ rects_out[n].y = rect->y;
+ rects_out[n].w = rect->x + rect->w - x1;
+ rects_out[n].h = rect->h;
+ n++;
+ }
+
+ if (rect->y < y0) {
+ rects_out[n].x = x0;
+ rects_out[n].y = rect->y;
+ rects_out[n].w = x1 - x0;
+ rects_out[n].h = y0 - rect->y;
+ n++;
+ }
+
+ if (y1 < rect->y + rect->h) {
+ rects_out[n].x = x0;
+ rects_out[n].y = y1;
+ rects_out[n].w = x1 - x0;
+ rects_out[n].h = rect->y + rect->h - y1;
+ n++;
+ }
+
+ return n;
+}
diff --git a/firmware/common/strlcat.c b/firmware/common/strlcat.c
index 783ea4daba..f1f0031f06 100644
--- a/firmware/common/strlcat.c
+++ b/firmware/common/strlcat.c
@@ -16,7 +16,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include <string.h>
+#include "string-extra.h"
/*
* Appends src to string dst of size siz (unlike strncat, siz is the
@@ -29,9 +29,7 @@ size_t
strlcat(char *dst, const char *src, size_t siz)
{
char *d = dst;
- const char *s = src;
- size_t n = siz;
- size_t dlen;
+ size_t dlen, n = siz;
/* Find the end of dst and adjust bytes left but don't go past end */
while (n-- != 0 && *d != '\0')
@@ -39,17 +37,6 @@ strlcat(char *dst, const char *src, size_t siz)
dlen = d - dst;
n = siz - dlen;
- if (n == 0)
- return(dlen + strlen(s));
- while (*s != '\0') {
- if (n != 1) {
- *d++ = *s;
- n--;
- }
- s++;
- }
- *d = '\0';
-
- return(dlen + (s - src)); /* count does not include NUL */
+ return strlcpy(dst + dlen, src, n) + dlen;
}
diff --git a/firmware/common/strlcpy.c b/firmware/common/strlcpy.c
index e320649140..5107ea1207 100644
--- a/firmware/common/strlcpy.c
+++ b/firmware/common/strlcpy.c
@@ -1,51 +1,39 @@
-/* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */
-
-/*
- * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
*
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
+ * Copyright (C) 2022 William Wilgus
*
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
+ * 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 <string.h>
+#include "strmemccpy.h"
/*
* Copy src to string dst of size siz. At most siz-1 characters
* will be copied. Always NUL terminates (unless siz == 0).
* Returns strlen(src); if retval >= siz, truncation occurred.
*/
-size_t
-strlcpy(char *dst, const char *src, size_t siz)
+size_t strlcpy(char *dst, const char *src, size_t siz)
{
- char *d = dst;
- const char *s = src;
- size_t n = siz;
-
- /* Copy as many bytes as will fit */
- if (n != 0) {
- while (--n != 0) {
- if ((*d++ = *s++) == '\0')
- break;
- }
- }
+ /* Copy as many bytes as will fit */
+ char *d = strmemccpy(dst, src, siz);
+ if (d)
+ return (d - dst - 1); /* count does not include NUL */
- /* Not enough room in dst, add NUL and traverse rest of src */
- if (n == 0) {
- if (siz != 0)
- *d = '\0'; /* NUL-terminate dst */
- while (*s++)
- ;
- }
-
- return(s - src - 1); /* count does not include NUL */
+ /* Not enough room in dst, traverse rest of src */
+ return(siz + strlen(src+siz)); /* count does not include NUL */
}
-
diff --git a/firmware/target/arm/imx233/samsung-ypz5/debug-ypz5.c b/firmware/common/strmemccpy.c
index c125eac973..830907f55e 100644
--- a/firmware/target/arm/imx233/samsung-ypz5/debug-ypz5.c
+++ b/firmware/common/strmemccpy.c
@@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Copyright (C) 2013 by Amaury Pouly
+ * Copyright (C) 2022 William Wilgus
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -18,12 +18,21 @@
* KIND, either express or implied.
*
****************************************************************************/
+/* (firmware/common/strmemccpy.c) */
+#include "string-extra.h"
-#include "system.h"
-#include "button-target.h"
-#include "lcd-target.h"
-
-bool dbg_hw_target_info(void)
+/* copies src to a buffer of len bytes stopping after
+ * len or the first NULL (\0) in src
+ * NULL terminates except when len = 0
+ * If len was exceeded NULL is returned otherwise returns
+ * a pointer to the first byte following the NULL in dst.
+*/
+char * strmemccpy(char *dst, const char *src, size_t len)
{
- return button_debug_screen() && lcd_debug_screen();
+ char * ret = (char *)memccpy(dst, src, '\0', len);
+ if (ret == NULL && len > 0)
+ {
+ dst[len - 1] = '\0';
+ }
+ return ret;
}
diff --git a/firmware/common/strnatcmp.c b/firmware/common/strnatcmp.c
index 0084ff3582..d77f7e995e 100644
--- a/firmware/common/strnatcmp.c
+++ b/firmware/common/strnatcmp.c
@@ -122,7 +122,8 @@ compare_left(char const *a, char const *b)
return 0;
}
-static int strnatcmp0(char const *a, char const *b, int fold_case)
+static int strnatcmp0(char const *a, char const *b,
+ int (*cmp_fn)(const char*, const char*))
{
int ai, bi;
int ca, cb;
@@ -150,13 +151,10 @@ static int strnatcmp0(char const *a, char const *b, int fold_case)
if (!ca && !cb) {
/* The strings compare the same. Call str[case]cmp() to ensure
consistent results. */
- if(fold_case)
- return strcasecmp(a,b);
- else
- return strcmp(a,b);
+ return cmp_fn(a,b);
}
- if (fold_case) {
+ if (cmp_fn == &strcasecmp) {
ca = nat_unify_case(ca);
cb = nat_unify_case(cb);
}
@@ -170,14 +168,12 @@ static int strnatcmp0(char const *a, char const *b, int fold_case)
}
}
-
-
int strnatcmp(const char *a, const char *b) {
- return strnatcmp0(a, b, 0);
+ return strnatcmp0(a, b, strcmp);
}
/* Compare, recognizing numeric string and ignoring case. */
int strnatcasecmp(const char *a, const char *b) {
- return strnatcmp0(a, b, 1);
+ return strnatcmp0(a, b, &strcasecmp);
}
diff --git a/firmware/common/strptokspn.c b/firmware/common/strptokspn.c
new file mode 100644
index 0000000000..16aafc66ef
--- /dev/null
+++ b/firmware/common/strptokspn.c
@@ -0,0 +1,77 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2022 by William WIlgus
+ *
+ * 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 <stddef.h>
+#include <string.h>
+#include "strtok_r.h"
+/* strptokspn_r is a custom implementation of strtok_r that does NOT modify
+ * the source string.
+ *
+ * strptokspn_r reads ptr as a series of zero or more tokens,
+ * and sep as delimiters of the tokens
+ * The tokens can be separated by one or more of the delimiters
+ * first call searches for the first token skipping over any leading delimiters.
+ * Returns pointer to first token
+ * Pointer *len contains the span to the first delimeter
+ * (this would be the resulting strlen had token actually been NULL terminated)
+ * Pointer **end contains pointer to first character after the last delimeter
+ *
+ * When strptokspn_r is called with a ptr == NULL, the next token is read from
+ * Pointer **end
+ *
+ * Note the returned token is NOT NULL terminated by the function as in strtok_r
+ * However the caller can use ret[len] = '\0'; to emulate a call to strtok_r
+*/
+const char *strptokspn_r(const char *ptr, const char *sep, size_t *len, const char **end)
+{
+ if (ptr == NULL) /* we got NULL input so then we get last position instead */
+ {
+ ptr = *end;
+ }
+
+ /* pass all letters that are including in the separator string */
+ while (*ptr && strchr(sep, *ptr))
+ ++ptr;
+
+ if (*ptr != '\0')
+ {
+ /* so this is where the next piece of string starts */
+ const char *start = ptr;
+ *len = strcspn(ptr, sep); /* Get span until any sep character in string */
+ *end = ptr + *len;
+ if (**end) /* the end is not a null byte */
+ ++*end;
+ return start;
+ }
+ return NULL;
+}
+
+#if !defined(HAVE_STRTOK_R)
+char * strtok_r(char *ptr, const char *sep, char **end)
+{
+ size_t len;
+ char * ret = (char*) strptokspn_r((const char*)ptr, sep, &len, (const char**) end);
+ if (ret)
+ ret[len] = '\0';
+ return ret;
+}
+#endif
diff --git a/firmware/common/structec.c b/firmware/common/structec.c
deleted file mode 100644
index fb13eaab51..0000000000
--- a/firmware/common/structec.c
+++ /dev/null
@@ -1,193 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2007 by Miika Pekkarinen
- *
- * 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 <ctype.h>
-#include <string.h>
-#include <inttypes.h>
-#include "structec.h"
-#include "system.h"
-#include "file.h"
-
-#define MAX_STRUCT_SIZE 128
-
-/**
- * Convert the struct endianess with the instructions provided.
- *
- * For example:
- * struct test {
- * long par1;
- * short par2;
- * short par3;
- * };
- *
- * structec_convert(instance_of_test, "lss", sizeof(struct test), true);
- *
- * Structures to be converted must be properly padded.
- *
- * @param structure Pointer to the struct being converted.
- * @param ecinst Instructions how to do the endianess conversion.
- * @param count Number of structures to write
- * @param enable Conversion is not made unless this is true.
- */
-void structec_convert(void *structure, const char *ecinst,
- long count, bool enable)
-{
- const char *ecinst_ring = ecinst;
- char *buf = (char *)structure;
-
- if (!enable)
- return;
-
- while (count > 0)
- {
- switch (*ecinst_ring)
- {
- /* Swap nothing. */
- case 'c':
- {
- buf++;
- break;
- }
-
- /* Swap 2 bytes. */
- case 's':
- {
- uint16_t *data = (uint16_t *)buf;
- *data = swap16(*data);
- buf += 2;
- break;
- }
-
- /* Swap 4 bytes. */
- case 'l':
- {
- uint32_t *data = (uint32_t *)buf;
- *data = swap32(*data);
- buf += 4;
- break;
- }
-
- /* Skip N bytes, idea taken from metadata.c */
- default:
- {
- if (isdigit(*ecinst_ring))
- buf += (*ecinst_ring - '0');
-
- break;
- }
- }
-
- ecinst_ring++;
- if (*ecinst_ring == '\0')
- {
- ecinst_ring = ecinst;
- count--;
- }
- }
-}
-
-/**
- * Determines the size of a struct in bytes by using endianess correction
- * string format.
- *
- * @param ecinst endianess correction string.
- * @return length of the struct in bytes.
- */
-static size_t structec_size(const char *ecinst)
-{
- size_t size = 0;
-
- do
- {
- switch (*ecinst)
- {
- case 'c': size += 1; break;
- case 's': size += 2; break;
- case 'l': size += 4; break;
- default:
- if (isdigit(*ecinst))
- size += (*ecinst - '0');
- }
- } while (*(++ecinst) != '\0');
-
- return size;
-}
-
-/**
- * Reads endianess corrected structure members from the given file.
- *
- * @param fd file descriptor of the file being read.
- * @param buf endianess corrected data is placed here.
- * @param scount the number of struct members to read.
- * @param ecinst endianess correction string.
- * @param ec if true, endianess correction is enabled.
- */
-ssize_t ecread(int fd, void *buf, size_t scount, const char *ecinst, bool ec)
-{
- ssize_t ret;
- size_t member_size = structec_size(ecinst);
-
- ret = read(fd, buf, scount * member_size);
- structec_convert(buf, ecinst, scount, ec);
-
- return ret;
-}
-
-/**
- * Writes endianess corrected structure members to the given file.
- *
- * @param fd file descriptor of the file being written to.
- * @param buf endianess corrected data is read here.
- * @param scount the number of struct members to write.
- * @param ecinst endianess correction string.
- * @param ec if true, endianess correction is enabled.
- */
-ssize_t ecwrite(int fd, const void *buf, size_t scount,
- const char *ecinst, bool ec)
-{
- char tmp[MAX_STRUCT_SIZE];
- ssize_t member_size = structec_size(ecinst);
-
- if (ec)
- {
- const char *p = (const char *)buf;
- int maxamount = (int)(MAX_STRUCT_SIZE / member_size);
- int i;
-
- for (i = 0; i < (long)scount; i += maxamount)
- {
- long amount = MIN((int)scount-i, maxamount);
-
- memcpy(tmp, p, member_size * amount);
- structec_convert(tmp, ecinst, amount, true);
- ssize_t ret = write(fd, tmp, amount * member_size);
-
- if(ret != amount * member_size)
- return ret;
-
- p += member_size * amount;
- }
-
- return scount * member_size;
- }
-
- return write(fd, buf, scount * member_size);
-}
diff --git a/firmware/common/timefuncs.c b/firmware/common/timefuncs.c
index 50addad27a..1405a8d926 100644
--- a/firmware/common/timefuncs.c
+++ b/firmware/common/timefuncs.c
@@ -32,6 +32,37 @@
static struct tm tm;
+time_t dostime_mktime(uint16_t dosdate, uint16_t dostime)
+{
+ /* this knows our mktime() only uses these struct tm fields */
+ struct tm tm;
+ tm.tm_sec = ((dostime ) & 0x1f) * 2;
+ tm.tm_min = ((dostime >> 5) & 0x3f);
+ tm.tm_hour = ((dostime >> 11) );
+ tm.tm_mday = ((dosdate ) & 0x1f);
+ tm.tm_mon = ((dosdate >> 5) & 0x0f) - 1;
+ tm.tm_year = ((dosdate >> 9) ) + 80;
+
+ return mktime(&tm);
+}
+
+void dostime_localtime(time_t time, uint16_t* dosdate, uint16_t* dostime)
+{
+ struct tm tm;
+#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
+ gmtime_r(&time, &tm);
+#else
+ localtime_r(&time, &tm);
+#endif
+
+ *dostime = ((tm.tm_sec / 2) << 0)|
+ ((tm.tm_min ) << 5)|
+ ((tm.tm_hour ) << 11);
+ *dosdate = ((tm.tm_mday ) << 0)|
+ ((tm.tm_mon + 1) << 5)|
+ ((tm.tm_year - 80) << 9);
+}
+
#if !CONFIG_RTC
static inline bool rtc_dirty(void)
{
@@ -55,13 +86,13 @@ static inline int rtc_read_datetime(struct tm *tm)
#if CONFIG_RTC
bool valid_time(const struct tm *tm)
{
- if (tm->tm_hour < 0 || tm->tm_hour > 23 ||
+ if (!tm || (tm->tm_hour < 0 || tm->tm_hour > 23 ||
tm->tm_sec < 0 || tm->tm_sec > 59 ||
tm->tm_min < 0 || tm->tm_min > 59 ||
tm->tm_year < 100 || tm->tm_year > 199 ||
tm->tm_mon < 0 || tm->tm_mon > 11 ||
tm->tm_wday < 0 || tm->tm_wday > 6 ||
- tm->tm_mday < 1 || tm->tm_mday > 31)
+ tm->tm_mday < 1 || tm->tm_mday > 31))
return false;
else
return true;
@@ -135,5 +166,21 @@ void set_day_of_week(struct tm *tm)
if(m == 0 || m == 1) y--;
tm->tm_wday = (d + mo[m] + y + y/4 - y/100 + y/400) % 7;
}
+
+void set_day_of_year(struct tm *tm)
+{
+ int y=tm->tm_year+1900;
+ int d=tm->tm_mday;
+ int m=tm->tm_mon;
+ d+=m*30;
+ if( ( (m>1) && !(y%4) ) && ( (y%100) || !(y%400) ) )
+ d++;
+ if(m>6)
+ {
+ d+=4;
+ m-=7;
+ }
+ tm->tm_yday = d + ((m+1) /2);
+}
#endif /* CONFIG_RTC */
diff --git a/firmware/common/ucl_decompress.c b/firmware/common/ucl_decompress.c
new file mode 100644
index 0000000000..3b43d76f9d
--- /dev/null
+++ b/firmware/common/ucl_decompress.c
@@ -0,0 +1,192 @@
+/* Standalone version of ucl_nrv2e_decompress_8 from UCL library
+ * Original copyright notice:
+ */
+/* n2e_d.c -- implementation of the NRV2E decompression algorithm
+
+ This file is part of the UCL data compression library.
+
+ Copyright (C) 1996-2002 Markus Franz Xaver Johannes Oberhumer
+ All Rights Reserved.
+
+ The UCL library 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.
+
+ The UCL library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the UCL library; see the file COPYING.
+ If not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Markus F.X.J. Oberhumer
+ <markus@oberhumer.com>
+ http://www.oberhumer.com/opensource/ucl/
+ */
+
+#include "ucl_decompress.h"
+
+#define UCL_UINT32_C(c) c ## U
+#define fail(x, r) do if(x) { *dst_len = olen; return r; } while(0)
+
+#define getbit(bb) \
+ (((bb = bb & 0x7f ? bb*2 : ((unsigned)src[ilen++]*2+1)) >> 8) & 1)
+
+int ucl_nrv2e_decompress_8(const uint8_t* src, uint32_t src_len,
+ uint8_t* dst, uint32_t* dst_len)
+{
+ uint32_t bb = 0;
+ uint32_t ilen = 0, olen = 0, last_m_off = 1;
+ uint32_t oend = *dst_len;
+
+ for (;;)
+ {
+ uint32_t m_off, m_len;
+
+ while (getbit(bb))
+ {
+ fail(ilen >= src_len, UCL_E_INPUT_OVERRUN);
+ fail(olen >= oend, UCL_E_OUTPUT_OVERRUN);
+ dst[olen++] = src[ilen++];
+ }
+ m_off = 1;
+ for (;;)
+ {
+ m_off = m_off*2 + getbit(bb);
+ fail(ilen >= src_len, UCL_E_INPUT_OVERRUN);
+ fail(m_off > UCL_UINT32_C(0xffffff) + 3, UCL_E_LOOKBEHIND_OVERRUN);
+ if (getbit(bb)) break;
+ m_off = (m_off-1)*2 + getbit(bb);
+ }
+ if (m_off == 2)
+ {
+ m_off = last_m_off;
+ m_len = getbit(bb);
+ }
+ else
+ {
+ fail(ilen >= src_len, UCL_E_INPUT_OVERRUN);
+ m_off = (m_off-3)*256 + src[ilen++];
+ if (m_off == UCL_UINT32_C(0xffffffff))
+ break;
+ m_len = (m_off ^ UCL_UINT32_C(0xffffffff)) & 1;
+ m_off >>= 1;
+ last_m_off = ++m_off;
+ }
+ if (m_len)
+ m_len = 1 + getbit(bb);
+ else if (getbit(bb))
+ m_len = 3 + getbit(bb);
+ else
+ {
+ m_len++;
+ do {
+ m_len = m_len*2 + getbit(bb);
+ fail(ilen >= src_len, UCL_E_INPUT_OVERRUN);
+ fail(m_len >= oend, UCL_E_OUTPUT_OVERRUN);
+ } while (!getbit(bb));
+ m_len += 3;
+ }
+ m_len += (m_off > 0x500);
+ fail(olen + m_len > oend, UCL_E_OUTPUT_OVERRUN);
+ fail(m_off > olen, UCL_E_LOOKBEHIND_OVERRUN);
+ {
+ const uint8_t *m_pos;
+ m_pos = dst + olen - m_off;
+ dst[olen++] = *m_pos++;
+ do dst[olen++] = *m_pos++; while (--m_len > 0);
+ }
+ }
+ *dst_len = olen;
+ return ilen == src_len ? UCL_E_OK : (ilen < src_len ? UCL_E_INPUT_NOT_CONSUMED : UCL_E_INPUT_OVERRUN);
+}
+
+static uint32_t xread32(const uint8_t* d)
+{
+ uint32_t r = 0;
+ r |= d[0] << 24;
+ r |= d[1] << 16;
+ r |= d[2] << 8;
+ r |= d[3] << 0;
+ return r;
+}
+
+/* From uclpack.c */
+int ucl_unpack(const uint8_t* src, uint32_t src_len,
+ uint8_t* dst, uint32_t* dst_len)
+{
+ static const uint8_t magic[8] =
+ {0x00, 0xe9, 0x55, 0x43, 0x4c, 0xff, 0x01, 0x1a};
+
+ /* make sure there are enough bytes for the header */
+ if(src_len < 18)
+ return UCL_E_BAD_MAGIC;
+
+ /* avoid memcmp for reasons of code size */
+ for(size_t i = 0; i < sizeof(magic); ++i)
+ if(src[i] != magic[i])
+ return UCL_E_BAD_MAGIC;
+
+ /* read the other header fields */
+ /* uint32_t flags = xread32(&src[8]); */
+ uint8_t method = src[12];
+ /* uint8_t level = src[13]; */
+ uint32_t block_size = xread32(&src[14]);
+
+ /* check supported compression method */
+ if(method != 0x2e)
+ return UCL_E_UNSUPPORTED_METHOD;
+
+ /* validate */
+ if(block_size < 1024 || block_size > 8*1024*1024)
+ return UCL_E_BAD_BLOCK_SIZE;
+
+ /* process the blocks */
+ src += 18;
+ src_len -= 18;
+ uint32_t dst_ilen = *dst_len;
+ while(1) {
+ if(src_len < 4)
+ return UCL_E_TRUNCATED;
+
+ uint32_t out_len = xread32(src); src += 4, src_len -= 4;
+ if(out_len == 0)
+ break;
+
+ if(src_len < 4)
+ return UCL_E_TRUNCATED;
+
+ uint32_t in_len = xread32(src); src += 4, src_len -= 4;
+ if(in_len > block_size || out_len > block_size ||
+ in_len == 0 || in_len > out_len)
+ return UCL_E_CORRUPTED;
+
+ if(src_len < in_len)
+ return UCL_E_TRUNCATED;
+
+ if(in_len < out_len) {
+ uint32_t actual_out_len = dst_ilen;
+ int rc = ucl_nrv2e_decompress_8(src, in_len, dst, &actual_out_len);
+ if(rc != UCL_E_OK)
+ return rc;
+ if(actual_out_len != out_len)
+ return UCL_E_CORRUPTED;
+ } else {
+ for(size_t i = 0; i < in_len; ++i)
+ dst[i] = src[i];
+ }
+
+ src += in_len;
+ src_len -= in_len;
+ dst += out_len;
+ dst_ilen -= out_len;
+ }
+
+ /* subtract leftover number of bytes to get size of compressed output */
+ *dst_len -= dst_ilen;
+ return UCL_E_OK;
+}
diff --git a/firmware/common/unicode.c b/firmware/common/unicode.c
index f0f663f712..1ed2e5e49d 100644
--- a/firmware/common/unicode.c
+++ b/firmware/common/unicode.c
@@ -166,15 +166,23 @@ static unsigned short default_cp_table_buf[MAX_CP_TABLE_SIZE+1];
default_cp_table_buf
#define cp_table_free(handle) \
do {} while (0)
-#define cp_table_alloc(filename, size, opsp) \
+#define cp_table_alloc(size, opsp) \
({ (void)(opsp); 1; })
+#define cp_table_pin(handle) \
+ do { (void)handle; } while(0)
+#define cp_table_unpin(handle) \
+ do { (void)handle; } while(0)
#else
-#define cp_table_alloc(filename, size, opsp) \
- core_alloc_ex((filename), (size), (opsp))
+#define cp_table_alloc(size, opsp) \
+ core_alloc_ex((size), (opsp))
#define cp_table_free(handle) \
core_free(handle)
#define cp_table_get_data(handle) \
core_get_data(handle)
+#define cp_table_pin(handle) \
+ core_pin(handle)
+#define cp_table_unpin(handle) \
+ core_unpin(handle)
#endif
static const unsigned char utf8comp[6] =
@@ -191,21 +199,8 @@ static inline void cptable_tohw16(uint16_t *buf, unsigned int count)
(void)buf; (void)count;
}
-static int move_callback(int handle, void *current, void *new)
-{
- /* we don't keep a pointer but we have to stop it if this applies to a
- buffer not yet swapped-in since it will likely be in use in an I/O
- call */
- return (handle != default_cp_handle || default_cp_table_ref != 0) ?
- BUFLIB_CB_CANNOT_MOVE : BUFLIB_CB_OK;
- (void)current; (void)new;
-}
-
static int alloc_and_load_cp_table(int cp, void *buf)
{
- static struct buflib_callbacks ops =
- { .move_callback = move_callback };
-
/* alloc and read only if there is an associated file */
const char *filename = cp_info[cp].filename;
if (!filename)
@@ -228,13 +223,17 @@ static int alloc_and_load_cp_table(int cp, void *buf)
!(size % (off_t)sizeof (uint16_t))) {
/* if the buffer is provided, use that but don't alloc */
- int handle = buf ? 0 : cp_table_alloc(filename, size, &ops);
- if (handle > 0)
+ int handle = buf ? 0 : cp_table_alloc(size, NULL);
+ if (handle > 0) {
+ cp_table_pin(handle);
buf = cp_table_get_data(handle);
+ }
if (buf && read(fd, buf, size) == size) {
close(fd);
cptable_tohw16(buf, size / sizeof (uint16_t));
+ if (handle > 0)
+ cp_table_unpin(handle);
return handle;
}
diff --git a/firmware/common/vuprintf.c b/firmware/common/vuprintf.c
index fb053ae7da..0566e3e37e 100644
--- a/firmware/common/vuprintf.c
+++ b/firmware/common/vuprintf.c
@@ -30,13 +30,10 @@
#ifndef BOOTLOADER
-/* Only the Quake plugin needs float formatting */
-#if defined(HAVE_LCD_COLOR) && \
- (!defined(LCD_STRIDEFORMAT) || (LCD_STRIDEFORMAT != VERTICAL_STRIDE)) && \
- (PLUGIN_BUFFER_SIZE > 0x14000) && (CONFIG_PLATFORM & PLATFORM_NATIVE) && defined(CPU_ARM)
-/* turn everything on */
-#define FMT_LENMOD (0xffffffff)
-#define FMT_RADIX (0xffffffff)
+/* Turn everything on if we have enough RAM. */
+#if MEMORYSIZE >= 8
+# define FMT_LENMOD (0xffffffff)
+# define FMT_RADIX (0xffffffff)
#endif
#endif
@@ -756,6 +753,7 @@ static int format_double_radix(double f,
if (prec_rem) {
prec_rem--;
}
+ /* fallthrough */
case 1: /* %e, %E */
explen = 2;
break;
diff --git a/firmware/common/zip.c b/firmware/common/zip.c
new file mode 100644
index 0000000000..2d560e472e
--- /dev/null
+++ b/firmware/common/zip.c
@@ -0,0 +1,875 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 by James Buren
+ *
+ * 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 "zip.h"
+#include "string-extra.h"
+#include "file.h"
+#include "dir.h"
+#include "system.h"
+#include "errno.h"
+#include "core_alloc.h"
+#include "timefuncs.h"
+#include "pathfuncs.h"
+#include "crc32.h"
+#include "rbendian.h"
+
+#define zip_core_alloc(N) core_alloc_ex((N),&buflib_ops_locked)
+
+enum {
+ ZIP_SIG_ED = 0x06054b50,
+ ZIP_SIG_CD = 0x02014b50,
+ ZIP_SIG_LF = 0x04034b50,
+ ZIP_BIT_DD = 0x0008,
+ ZIP_METHOD_STORE = 0x0000,
+ ZIP_MAX_LENGTH = 0xffff,
+ ZIP_BUFFER_SIZE = 4096,
+};
+
+enum {
+ ZIP_STATE_INITIAL,
+ ZIP_STATE_ED_ENTER,
+ ZIP_STATE_ED_EXIT,
+ ZIP_STATE_CD_ENTER,
+ ZIP_STATE_CD_EXIT,
+ ZIP_STATE_LF_ENTER,
+ ZIP_STATE_LF_EXIT,
+};
+
+struct zip_ed_disk {
+ uint32_t signature;
+ uint16_t disk_number;
+ uint16_t disk_number_cd;
+ uint16_t disk_entries_cd;
+ uint16_t cd_entries;
+ uint32_t cd_size;
+ uint32_t cd_offset;
+ uint16_t comment_length;
+ // comment block (variable length)
+} __attribute__((packed));
+
+struct zip_ed {
+ uint32_t cd_size;
+ uint32_t cd_offset;
+ uint16_t cd_entries;
+};
+
+struct zip_cd_disk {
+ uint32_t signature;
+ uint16_t version_madeby;
+ uint16_t version_needed;
+ uint16_t flags;
+ uint16_t method;
+ uint16_t time;
+ uint16_t date;
+ uint32_t crc;
+ uint32_t compressed_size;
+ uint32_t uncompressed_size;
+ uint16_t name_length;
+ uint16_t extra_length;
+ uint16_t comment_length;
+ uint16_t disk_number;
+ uint16_t internal_attributes;
+ uint32_t external_attributes;
+ uint32_t lf_offset;
+ // name block (variable length)
+ // extra block (variable length)
+ // comment block (variable length)
+} __attribute__((packed));
+
+struct zip_cd {
+ uint32_t crc;
+ uint32_t compressed_size;
+ uint32_t uncompressed_size;
+ uint32_t lf_offset;
+};
+
+struct zip_lf_disk {
+ uint32_t signature;
+ uint16_t version_needed;
+ uint16_t flags;
+ uint16_t method;
+ uint16_t time;
+ uint16_t date;
+ uint32_t crc;
+ uint32_t compressed_size;
+ uint32_t uncompressed_size;
+ uint16_t name_length;
+ uint16_t extra_length;
+ // name block (variable length)
+ // extra block (variable length)
+} __attribute__((packed));
+
+struct zip_lf {
+ uint16_t flags;
+ uint16_t method;
+ uint16_t time;
+ uint16_t date;
+ uint32_t crc;
+ uint32_t compressed_size;
+ uint32_t uncompressed_size;
+ uint16_t name_length;
+ char name[MAX_PATH];
+};
+
+struct zip {
+ ssize_t (*read) (struct zip*, void*, size_t);
+ off_t (*seek) (struct zip*, off_t, int);
+ off_t (*size) (struct zip*);
+ void (*close) (struct zip*);
+ int zip_handle;
+ int state;
+ zip_callback cb;
+ struct zip_args args;
+ void* ctx;
+ struct zip_ed ed;
+ int cds_handle;
+ struct zip_cd* cds;
+ struct zip_lf lf;
+};
+
+struct zip_file {
+ struct zip base;
+ int file;
+};
+
+struct zip_mem {
+ struct zip base;
+ int mem_handle;
+ const uint8_t* mem;
+ off_t mem_offset;
+ off_t mem_size;
+};
+
+struct zip_extract {
+ zip_callback cb;
+ void* ctx;
+ size_t name_offset;
+ size_t name_size;
+ char* name;
+ int file;
+ char path[MAX_PATH];
+};
+
+static int zip_read_ed(struct zip* z) {
+ const off_t file_size = z->size(z);
+ const off_t max_size = sizeof(struct zip_ed_disk) + ZIP_MAX_LENGTH;
+ const off_t read_size = MIN(file_size, max_size);
+ const uint32_t sig = htole32(ZIP_SIG_ED);
+ int mem_handle = -1;
+ uint8_t* mem;
+ off_t i = read_size - sizeof(struct zip_ed_disk);
+ const struct zip_ed_disk* edd;
+ uint16_t disk_number;
+ uint16_t disk_number_cd;
+ uint16_t disk_entries_cd;
+ uint16_t cd_entries;
+ struct zip_ed* ed = &z->ed;
+ int rv;
+
+ z->state = ZIP_STATE_ED_ENTER;
+
+ if (file_size < (off_t) sizeof(struct zip_ed_disk)) {
+ rv = -2;
+ goto bail;
+ }
+
+ if ((mem_handle = zip_core_alloc(read_size)) < 0) {
+ rv = -3;
+ goto bail;
+ }
+
+ mem = core_get_data(mem_handle);
+
+ if (z->seek(z, -read_size, SEEK_END) < 0) {
+ rv = -4;
+ goto bail;
+ }
+
+ if (z->read(z, mem, read_size) != read_size) {
+ rv = -5;
+ goto bail;
+ }
+
+ for (; i >= 0; i--)
+ if (memcmp(mem + i, &sig, sizeof(uint32_t)) == 0)
+ break;
+
+ if (i < 0) {
+ rv = -6;
+ goto bail;
+ }
+
+ edd = (struct zip_ed_disk*) (mem + i);
+ disk_number = letoh16(edd->disk_number);
+ disk_number_cd = letoh16(edd->disk_number_cd);
+ disk_entries_cd = letoh16(edd->disk_entries_cd);
+ cd_entries = letoh16(edd->cd_entries);
+
+ if (disk_number != 0 || disk_number_cd != 0 || disk_entries_cd != cd_entries) {
+ rv = -7;
+ goto bail;
+ }
+
+ ed->cd_size = letoh32(edd->cd_size);
+ ed->cd_offset = letoh32(edd->cd_offset);
+ ed->cd_entries = cd_entries;
+
+ z->state = ZIP_STATE_ED_EXIT;
+ rv = 0;
+
+bail:
+ core_free(mem_handle);
+ return rv;
+}
+
+static int zip_read_cd(struct zip* z, bool use_cb) {
+ const struct zip_ed* ed = &z->ed;
+ const uint32_t cd_size = ed->cd_size;
+ const uint32_t cd_offset = ed->cd_offset;
+ const uint16_t cd_entries = ed->cd_entries;
+ const uint32_t sig = htole32(ZIP_SIG_CD);
+ int cds_handle = -1;
+ int mem_handle = -1;
+ struct zip_cd* cds;
+ uint8_t* mem;
+ struct zip_lf* lf = &z->lf;
+ struct zip_args* args = &z->args;
+ struct zip_cd_disk* cdd;
+ struct zip_cd* cd;
+ uint16_t name_length;
+ int rv;
+
+ z->state = ZIP_STATE_CD_ENTER;
+
+ if ((cds_handle = zip_core_alloc(sizeof(struct zip_cd) * cd_entries)) < 0) {
+ rv = -7;
+ goto bail;
+ }
+
+ if ((mem_handle = zip_core_alloc(cd_size)) < 0) {
+ rv = -8;
+ goto bail;
+ }
+
+ cds = core_get_data(cds_handle);
+ mem = core_get_data(mem_handle);
+
+ if (z->seek(z, cd_offset, SEEK_SET) < 0) {
+ rv = -9;
+ goto bail;
+ }
+
+ if (z->read(z, mem, cd_size) != (ssize_t) cd_size) {
+ rv = -10;
+ goto bail;
+ }
+
+ if (use_cb) {
+ args->entries = cd_entries;
+ args->name = lf->name;
+ args->block = NULL;
+ args->block_size = 0;
+ args->read_size = 0;
+ }
+
+ cdd = (struct zip_cd_disk*) mem;
+
+ for (uint16_t i = 0; i < cd_entries; i++) {
+ if (cdd->signature != sig) {
+ rv = -11;
+ goto bail;
+ }
+
+ cd = &cds[i];
+
+ cd->crc = letoh32(cdd->crc);
+ cd->compressed_size = letoh32(cdd->compressed_size);
+ cd->uncompressed_size = letoh32(cdd->uncompressed_size);
+ cd->lf_offset = letoh32(cdd->lf_offset);
+
+ mem += sizeof(struct zip_cd_disk);
+ name_length = letoh16(cdd->name_length);
+ if (use_cb) {
+ if (name_length >= sizeof(lf->name)) {
+ rv = -12;
+ goto bail;
+ }
+
+ args->entry = i + 1;
+ args->file_size = cd->uncompressed_size;
+ args->mtime = dostime_mktime(letoh16(cdd->date), letoh16(cdd->time));
+
+ memcpy(lf->name, mem, name_length);
+ lf->name[name_length] = '\0';
+
+ if ((rv = z->cb(args, ZIP_PASS_SHALLOW, z->ctx)) > 0)
+ goto bail;
+ }
+ mem += name_length;
+ mem += letoh16(cdd->extra_length);
+ mem += letoh16(cdd->comment_length);
+ cdd = (struct zip_cd_disk*) mem;
+ }
+
+ z->cds_handle = cds_handle;
+ z->cds = cds;
+ z->state = ZIP_STATE_CD_EXIT;
+ rv = 0;
+
+bail:
+ if (rv != 0)
+ core_free(cds_handle);
+ core_free(mem_handle);
+ return rv;
+}
+
+static int zip_read_lf(struct zip* z, uint16_t i) {
+ const uint32_t sig = htole32(ZIP_SIG_LF);
+ const struct zip_cd* cd = &z->cds[i];
+ struct zip_lf* lf = &z->lf;
+ struct zip_lf_disk lfd;
+ uint16_t name_length;
+
+ if (z->seek(z, cd->lf_offset, SEEK_SET) < 0)
+ return -14;
+
+ if (z->read(z, &lfd, sizeof(struct zip_lf_disk)) != sizeof(struct zip_lf_disk))
+ return -15;
+
+ if (lfd.signature != sig)
+ return -16;
+
+ name_length = letoh16(lfd.name_length);
+
+ if (name_length >= sizeof(lf->name))
+ return -17;
+
+ if (z->read(z, lf->name, name_length) != name_length)
+ return -18;
+
+ if (z->seek(z, letoh16(lfd.extra_length), SEEK_CUR) < 0)
+ return -19;
+
+ lf->flags = letoh16(lfd.flags);
+ lf->method = letoh16(lfd.method);
+ lf->time = letoh16(lfd.time);
+ lf->date = letoh16(lfd.date);
+ lf->crc = letoh32(lfd.crc);
+ lf->compressed_size = letoh32(lfd.compressed_size);
+ lf->uncompressed_size = letoh32(lfd.uncompressed_size);
+ lf->name_length = name_length;
+ lf->name[name_length] = '\0';
+
+ if ((lf->flags & ZIP_BIT_DD) == ZIP_BIT_DD) {
+ lf->crc = cd->crc;
+ lf->compressed_size = cd->compressed_size;
+ lf->uncompressed_size = cd->uncompressed_size;
+ }
+
+ return 0;
+}
+
+static int zip_read_store(struct zip* z, void* mem, uint32_t mem_size) {
+ const struct zip_lf* lf = &z->lf;
+ struct zip_args* args = &z->args;
+ uint32_t file_size = lf->uncompressed_size;
+ uint32_t block_size = mem_size;
+ uint32_t crc = 0xffffffff;
+ int rv;
+
+ if (lf->compressed_size != lf->uncompressed_size)
+ return -21;
+
+ args->block = mem;
+ args->block_size = block_size;
+ args->read_size = 0;
+
+ do {
+ if (block_size > file_size) {
+ args->block_size = block_size = file_size;
+ }
+
+ if (z->read(z, mem, block_size) != (off_t) block_size)
+ return -22;
+
+ args->read_size += block_size;
+ crc = crc_32r(mem, block_size, crc);
+
+ if ((rv = z->cb(args, ZIP_PASS_DATA, z->ctx)) != 0)
+ return (rv < 0) ? 0 : rv;
+
+ file_size -= block_size;
+ } while (file_size > 0);
+
+ if (~crc != lf->crc)
+ return -24;
+
+ return 0;
+}
+
+static int zip_read_entry(struct zip* z, uint16_t i, void* mem, uint32_t mem_size) {
+ const struct zip_lf* lf = &z->lf;
+ struct zip_args* args = &z->args;
+ int rv;
+
+ if ((rv = zip_read_lf(z, i)) != 0)
+ return rv;
+
+ args->entry = i + 1;
+ args->file_size = lf->uncompressed_size;
+ args->mtime = dostime_mktime(lf->date, lf->time);
+ args->block = NULL;
+ args->block_size = 0;
+ args->read_size = 0;
+
+ if ((rv = z->cb(&z->args, ZIP_PASS_START, z->ctx)) != 0)
+ return (rv < 0) ? 0 : rv;
+
+ if (lf->uncompressed_size == 0)
+ goto skip_data;
+
+ if (lf->method == ZIP_METHOD_STORE) {
+ if ((rv = zip_read_store(z, mem, mem_size)) != 0)
+ return rv;
+ } else {
+ return -20;
+ }
+
+skip_data:
+ args->block = NULL;
+ args->block_size = 0;
+ args->read_size = 0;
+
+ if ((rv = z->cb(args, ZIP_PASS_END, z->ctx)) != 0)
+ return (rv < 0) ? 0 : rv;
+
+ return 0;
+}
+
+static int zip_read_entries(struct zip* z) {
+ const struct zip_ed* ed = &z->ed;
+ const uint16_t cd_entries = ed->cd_entries;
+ struct zip_lf* lf = &z->lf;
+ struct zip_args* args = &z->args;
+ uint32_t mem_size = ZIP_BUFFER_SIZE;
+ int mem_handle;
+ void* mem;
+ int rv;
+
+ z->state = ZIP_STATE_LF_ENTER;
+
+ if ((mem_handle = zip_core_alloc(mem_size)) < 0) {
+ rv = -13;
+ goto bail;
+ }
+
+ mem = core_get_data(mem_handle);
+
+ args->entries = cd_entries;
+ args->name = lf->name;
+
+ for (uint16_t i = 0; i < cd_entries; i++)
+ if ((rv = zip_read_entry(z, i, mem, mem_size)) > 0)
+ goto bail;
+
+ z->state = ZIP_STATE_LF_EXIT;
+ rv = 0;
+
+bail:
+ core_free(mem_handle);
+ return rv;
+}
+
+static void zip_init(struct zip* z, int zip_handle) {
+ z->zip_handle = zip_handle;
+ z->state = ZIP_STATE_INITIAL;
+ z->cb = NULL;
+ memset(&z->args, 0, sizeof(struct zip_args));
+ z->ctx = NULL;
+ memset(&z->ed, 0, sizeof(struct zip_ed));
+ z->cds_handle = -1;
+ z->cds = NULL;
+ memset(&z->lf, 0, sizeof(struct zip_lf));
+}
+
+static ssize_t zip_file_read(struct zip* zh, void* mem, size_t mem_size) {
+ struct zip_file* z = (struct zip_file*) zh;
+
+ return read(z->file, mem, mem_size);
+}
+
+static off_t zip_file_seek(struct zip* zh, off_t offset, int whence) {
+ struct zip_file* z = (struct zip_file*) zh;
+
+ return lseek(z->file, offset, whence);
+}
+
+static off_t zip_file_size(struct zip* zh) {
+ struct zip_file* z = (struct zip_file*) zh;
+
+ return filesize(z->file);
+}
+
+static void zip_file_close(struct zip* zh) {
+ struct zip_file* z = (struct zip_file*) zh;
+
+ close(z->file);
+}
+
+static void zip_file_init(struct zip_file* z, int zip_handle, int file) {
+ struct zip* zh = &z->base;
+
+ zh->read = zip_file_read;
+ zh->seek = zip_file_seek;
+ zh->size = zip_file_size;
+ zh->close = zip_file_close;
+ zip_init(zh, zip_handle);
+
+ z->file = file;
+}
+
+static ssize_t zip_mem_read(struct zip* zh, void* mem, size_t mem_size) {
+ struct zip_mem* z = (struct zip_mem*) zh;
+ off_t bytes = z->mem_size - z->mem_offset;
+ off_t read_size = MIN(bytes, (off_t) mem_size);
+
+ memcpy(mem, z->mem + z->mem_offset, read_size);
+ z->mem_offset += read_size;
+
+ return read_size;
+}
+
+static off_t zip_mem_seek(struct zip* zh, off_t offset, int whence) {
+ struct zip_mem* z = (struct zip_mem*) zh;
+ off_t new_offset;
+
+ switch (whence) {
+ case SEEK_SET:
+ new_offset = offset;
+ break;
+
+ case SEEK_CUR:
+ new_offset = z->mem_offset + offset;
+ break;
+
+ case SEEK_END:
+ new_offset = z->mem_size + offset;
+ break;
+
+ default:
+ new_offset = -1;
+ break;
+ }
+
+ if (new_offset < 0 || new_offset > z->mem_size)
+ return -1;
+
+ z->mem_offset = new_offset;
+
+ return new_offset;
+}
+
+static off_t zip_mem_size(struct zip* zh) {
+ struct zip_mem* z = (struct zip_mem*) zh;
+
+ return z->mem_size;
+}
+
+static void zip_mem_close(struct zip* zh) {
+ struct zip_mem* z = (struct zip_mem*) zh;
+
+ core_free(z->mem_handle);
+}
+
+static void zip_mem_init(struct zip_mem* z, int zip_handle, int mem_handle, const void* mem, off_t mem_size) {
+ struct zip* zh = &z->base;
+
+ zh->read = zip_mem_read;
+ zh->seek = zip_mem_seek;
+ zh->size = zip_mem_size;
+ zh->close = zip_mem_close;
+ zip_init(zh, zip_handle);
+
+ z->mem_handle = mem_handle;
+ z->mem = mem;
+ z->mem_offset = 0;
+ z->mem_size = mem_size;
+}
+
+static int zip_extract_start(const struct zip_args* args, struct zip_extract* ze) {
+ size_t name_length;
+ const char* dir;
+ size_t dir_length;
+
+ if ((name_length = strlcpy(ze->name, args->name, ze->name_size)) >= ze->name_size)
+ return 5;
+
+ if ((dir_length = path_dirname(ze->name, &dir)) > 0) {
+ char c = ze->name[dir_length];
+
+ ze->name[dir_length] = '\0';
+
+ if (!dir_exists(ze->path)) {
+ const char* path = ze->name;
+ const char* name;
+
+ while (parse_path_component(&path, &name) > 0) {
+ size_t offset = path - ze->name;
+ char c = ze->name[offset];
+
+ ze->name[offset] = '\0';
+
+ if (mkdir(ze->path) < 0 && errno != EEXIST)
+ return 6;
+
+ ze->name[offset] = c;
+ }
+ }
+
+ ze->name[dir_length] = c;
+ }
+
+ if (ze->name[name_length - 1] == PATH_SEPCH) {
+ if (mkdir(ze->path) < 0 && errno != EEXIST)
+ return 7;
+
+ return 0;
+ }
+
+ if ((ze->file = creat(ze->path, 0666)) < 0)
+ return 8;
+
+ return 0;
+}
+
+static int zip_extract_data(const struct zip_args* args, struct zip_extract* ze) {
+ if (write(ze->file, args->block, args->block_size) != (ssize_t) args->block_size) {
+ return 9;
+ }
+
+ return 0;
+}
+
+static int zip_extract_end(const struct zip_args* args, struct zip_extract* ze) {
+ int rv;
+
+ if (ze->file >= 0) {
+ rv = close(ze->file);
+
+ ze->file = -1;
+
+ if (rv < 0)
+ return 10;
+ }
+
+ if (modtime(ze->path, args->mtime) < 0)
+ return 11;
+
+ return 0;
+}
+
+static int zip_extract_callback(const struct zip_args* args, int pass, void* ctx) {
+ struct zip_extract* ze = ctx;
+ int rv;
+
+ if (ze->cb != NULL && (rv = ze->cb(args, pass, ze->ctx)) != 0)
+ return rv;
+
+ switch (pass) {
+ case ZIP_PASS_START:
+ return zip_extract_start(args, ze);
+
+ case ZIP_PASS_DATA:
+ return zip_extract_data(args, ze);
+
+ case ZIP_PASS_END:
+ return zip_extract_end(args, ze);
+
+ default:
+ return 1;
+ }
+}
+
+struct zip* zip_open(const char* name, bool try_mem) {
+ int file = -1;
+ int mem_handle = -1;
+ int zip_handle = -1;
+ off_t mem_size;
+ void* mem;
+ void* zip;
+
+ if (name == NULL || name[0] == '\0')
+ goto bail;
+
+ if ((file = open(name, O_RDONLY)) < 0)
+ goto bail;
+
+ if (try_mem && (mem_handle = zip_core_alloc(mem_size = filesize(file))) >= 0) {
+ if ((zip_handle = zip_core_alloc(sizeof(struct zip_mem))) < 0)
+ goto bail;
+
+ mem = core_get_data(mem_handle);
+
+ if (read(file, mem, mem_size) != mem_size)
+ goto bail;
+
+ close(file);
+
+ zip = core_get_data(zip_handle);
+
+ zip_mem_init(zip, zip_handle, mem_handle, mem, mem_size);
+ } else {
+ if ((zip_handle = zip_core_alloc(sizeof(struct zip_file))) < 0)
+ goto bail;
+
+ zip = core_get_data(zip_handle);
+
+ zip_file_init(zip, zip_handle, file);
+ }
+
+ return zip;
+
+bail:
+ if (file >= 0)
+ close(file);
+ core_free(mem_handle);
+ core_free(zip_handle);
+ return NULL;
+}
+
+int zip_read_shallow(struct zip* z, zip_callback cb, void* ctx) {
+ int rv;
+
+ if (z == NULL || z->state != ZIP_STATE_INITIAL || cb == NULL)
+ return -1;
+
+ z->cb = cb;
+ z->ctx = ctx;
+
+ if ((rv = zip_read_ed(z)) != 0)
+ return rv;
+
+ return zip_read_cd(z, true);
+}
+
+int zip_read_deep(struct zip* z, zip_callback cb, void* ctx) {
+ int rv;
+
+ if (z == NULL || (z->state != ZIP_STATE_INITIAL && z->state != ZIP_STATE_CD_EXIT) || cb == NULL)
+ return -1;
+
+ z->cb = cb;
+ z->ctx = ctx;
+
+ if (z->state == ZIP_STATE_CD_EXIT)
+ goto read_entries;
+
+ if ((rv = zip_read_ed(z)) != 0)
+ return rv;
+
+ if ((rv = zip_read_cd(z, false)) != 0)
+ return rv;
+
+read_entries:
+ return zip_read_entries(z);
+}
+
+int zip_extract(struct zip* z, const char* root, zip_callback cb, void* ctx) {
+ int rv;
+ int ze_handle = -1;
+ struct zip_extract* ze;
+ char* path;
+ size_t size;
+ size_t length;
+
+ if (root == NULL || root[0] == '\0')
+ root = PATH_ROOTSTR;
+
+ if (root[0] != PATH_SEPCH) {
+ rv = -1;
+ goto bail;
+ }
+
+ if (!dir_exists(root)) {
+ rv = 1;
+ goto bail;
+ }
+
+ if ((ze_handle = zip_core_alloc(sizeof(struct zip_extract))) < 0) {
+ rv = 2;
+ goto bail;
+ }
+
+ ze = core_get_data(ze_handle);
+ ze->cb = cb;
+ ze->ctx = ctx;
+ ze->file = -1;
+
+ path = ze->path;
+ size = sizeof(ze->path);
+ length = strlcpy(path, root, size);
+
+ if (length >= size) {
+ rv = 3;
+ goto bail;
+ }
+
+ path += length;
+ size -= length;
+
+ if (path[-1] != PATH_SEPCH) {
+ length = strlcpy(path, PATH_SEPSTR, size);
+
+ if (length >= size) {
+ rv = 4;
+ goto bail;
+ }
+
+ path += length;
+ size -= length;
+ }
+
+ ze->name_offset = path - ze->path;
+ ze->name_size = size;
+ ze->name = path;
+
+ rv = zip_read_deep(z, zip_extract_callback, ze);
+
+bail:
+ if (ze_handle >= 0) {
+ if (ze->file >= 0)
+ close(ze->file);
+
+ core_free(ze_handle);
+ }
+ return rv;
+}
+
+void zip_close(struct zip* z) {
+ if (z == NULL)
+ return;
+
+ z->close(z);
+
+ core_free(z->cds_handle);
+
+ core_free(z->zip_handle);
+}
diff --git a/firmware/core_alloc.c b/firmware/core_alloc.c
index df1b4d3213..948911b973 100644
--- a/firmware/core_alloc.c
+++ b/firmware/core_alloc.c
@@ -8,24 +8,38 @@
/* not static so it can be discovered by core_get_data() */
struct buflib_context core_ctx;
-/* defined in linker script */
#if (CONFIG_PLATFORM & PLATFORM_NATIVE) && !defined(__PCTOOL__)
+
#if defined(IPOD_VIDEO) && !defined(BOOTLOADER)
+/* defined in linker script */
+extern unsigned char audiobuffer[];
extern unsigned char *audiobufend_lds[];
+/* pointer to end of audio buffer filled at runtime allocator_init */
unsigned char *audiobufend;
-#else /* !IPOD_VIDEO */
-extern unsigned char audiobufend[];
-#endif
+#elif defined(SANSA_E200) && defined(HAVE_BOOTLOADER_USB_MODE)
+/* defined in linker script */
+extern unsigned char freebuffer[];
+extern unsigned char freebufferend[];
+/* map linker symbol to the audiobuffer in order to use core_alloc */
+unsigned char *audiobuffer = (unsigned char *)freebuffer;
+unsigned char *audiobufend = (unsigned char *)freebufferend;
+#else /* !IPOD_VIDEO, !SANSA_E200&&BOOTLOADERUSB */
/* defined in linker script */
extern unsigned char audiobuffer[];
+extern unsigned char audiobufend[];
+#endif
+
#else /* PLATFORM_HOSTED */
static unsigned char audiobuffer[((MEMORYSIZE)*1024-768)*1024];
unsigned char *audiobufend = audiobuffer + sizeof(audiobuffer);
extern unsigned char *audiobufend;
#endif
+#ifdef BUFLIB_DEBUG_PRINT
/* debug test alloc */
static int test_alloc;
+#endif
+
void core_allocator_init(void)
{
unsigned char *start = ALIGN_UP(audiobuffer, sizeof(intptr_t));
@@ -40,16 +54,9 @@ void core_allocator_init(void)
buflib_init(&core_ctx, start, audiobufend - start);
- test_alloc = core_alloc("test", 112);
-}
-
-bool core_test_free(void)
-{
- bool ret = test_alloc > 0;
- if (ret)
- test_alloc = core_free(test_alloc);
-
- return ret;
+#ifdef BUFLIB_DEBUG_PRINT
+ test_alloc = core_alloc(112);
+#endif
}
/* Allocate memory in the "core" context. See documentation
@@ -58,14 +65,14 @@ bool core_test_free(void)
* Note: Buffers allocated by this functions are movable.
* Don't pass them to functions that call yield()
* like disc input/output. */
-int core_alloc(const char* name, size_t size)
+int core_alloc(size_t size)
{
- return buflib_alloc_ex(&core_ctx, size, name, NULL);
+ return buflib_alloc_ex(&core_ctx, size, NULL);
}
-int core_alloc_ex(const char* name, size_t size, struct buflib_callbacks *ops)
+int core_alloc_ex(size_t size, struct buflib_callbacks *ops)
{
- return buflib_alloc_ex(&core_ctx, size, name, ops);
+ return buflib_alloc_ex(&core_ctx, size, ops);
}
size_t core_available(void)
@@ -83,9 +90,9 @@ int core_free(int handle)
return buflib_free(&core_ctx, handle);
}
-int core_alloc_maximum(const char* name, size_t *size, struct buflib_callbacks *ops)
+int core_alloc_maximum(size_t *size, struct buflib_callbacks *ops)
{
- return buflib_alloc_maximum(&core_ctx, name, size, ops);
+ return buflib_alloc_maximum(&core_ctx, size, ops);
}
bool core_shrink(int handle, void* new_start, size_t new_size)
@@ -93,23 +100,43 @@ bool core_shrink(int handle, void* new_start, size_t new_size)
return buflib_shrink(&core_ctx, handle, new_start, new_size);
}
-const char* core_get_name(int handle)
+void core_pin(int handle)
+{
+ buflib_pin(&core_ctx, handle);
+}
+
+void core_unpin(int handle)
{
- const char *name = buflib_get_name(&core_ctx, handle);
- return name ?: "<anonymous>";
+ buflib_unpin(&core_ctx, handle);
}
+unsigned core_pin_count(int handle)
+{
+ return buflib_pin_count(&core_ctx, handle);
+}
+
+#ifdef BUFLIB_DEBUG_PRINT
int core_get_num_blocks(void)
{
return buflib_get_num_blocks(&core_ctx);
}
-void core_print_block_at(int block_num, char* buf, size_t bufsize)
+bool core_print_block_at(int block_num, char* buf, size_t bufsize)
{
- buflib_print_block_at(&core_ctx, block_num, buf, bufsize);
+ return buflib_print_block_at(&core_ctx, block_num, buf, bufsize);
}
-#ifdef DEBUG
+bool core_test_free(void)
+{
+ bool ret = test_alloc > 0;
+ if (ret)
+ test_alloc = core_free(test_alloc);
+
+ return ret;
+}
+#endif
+
+#ifdef BUFLIB_DEBUG_CHECK_VALID
void core_check_valid(void)
{
buflib_check_valid(&core_ctx);
diff --git a/firmware/drivers/ata.c b/firmware/drivers/ata.c
index 3514511270..1c85b7bd5f 100644
--- a/firmware/drivers/ata.c
+++ b/firmware/drivers/ata.c
@@ -36,14 +36,6 @@
#include "storage.h"
#include "logf.h"
-/* The FC1307A ATA->SD chipset (used by the common iFlash adapters)
- doesn't support mandatory ATA power management commands. Unfortunately
- simply gating off the SLEEP command isn't sufficient; we need to
- disable advanced powersaving entirely because otherwise we might
- kill power before the device has finished flusing writes.
-*/
-//#define FC1307A_WORKAROUND
-
#define SELECT_DEVICE1 0x10
#define SELECT_LBA 0x40
@@ -58,10 +50,14 @@
#define CMD_WRITE_MULTIPLE 0xC5
#define CMD_WRITE_MULTIPLE_EXT 0x39
#define CMD_SET_MULTIPLE_MODE 0xC6
+#ifdef HAVE_ATA_SMART
+#define CMD_SMART 0xB0
+#endif
#define CMD_STANDBY_IMMEDIATE 0xE0
#define CMD_STANDBY 0xE2
#define CMD_IDENTIFY 0xEC
#define CMD_SLEEP 0xE6
+#define CMD_FLUSH_CACHE 0xE7
#define CMD_SET_FEATURES 0xEF
#define CMD_SECURITY_FREEZE_LOCK 0xF5
#ifdef HAVE_ATA_DMA
@@ -75,7 +71,6 @@
#ifdef HAVE_ATA_POWER_OFF
#define ATA_POWER_OFF_TIMEOUT 2*HZ
-#define ATA_POWER_OFF_TIMEOUT_NOPM 5*HZ
#endif
#if defined(HAVE_USBSTACK)
@@ -160,12 +155,8 @@ static inline bool ata_sleep_timed_out(void)
static inline void schedule_ata_power_off(void)
{
#ifdef HAVE_ATA_POWER_OFF
- power_off_tick = current_tick;
- /* If our device doesn't support SLEEP give a bit more time to flush */
- if (!(identify_info[82] & (1 << 3)))
- power_off_tick += ATA_POWER_OFF_TIMEOUT_NOPM;
- else
- power_off_tick += ATA_POWER_OFF_TIMEOUT;
+ if (!ata_disk_can_poweroff())
+ power_off_tick = current_tick + ATA_POWER_OFF_TIMEOUT;
#endif
}
@@ -239,12 +230,6 @@ static int ata_perform_wakeup(int state)
static int ata_perform_sleep(void)
{
- /* Don't issue the sleep command if the device
- doesn't support (mandatory!) ATA power management commands!
- */
- if (!(identify_info[82] & (1 << 3)))
- return 0;
-
logf("ata SLEEP %ld", current_tick);
ATA_OUT8(ATA_SELECT, ata_device);
@@ -254,10 +239,25 @@ static int ata_perform_sleep(void)
return -1;
}
- ATA_OUT8(ATA_COMMAND, CMD_SLEEP);
+ /* STANDBY IMMEDIATE
+ - writes all cached data
+ - transitions to PM2:Standby
+ - enters Standby_z power condition
- if (!wait_for_rdy())
- {
+ This places the device into a state where power-off is safe, but
+ it is not the lowest-theoretical power state -- that is SLEEP, but
+ that is bugged on some SSDs (FC1307A-based).
+
+ TODO: Is there a practical downside to using STANDBY_IMMEDIATE instead
+ of SLEEP, assuming the former spins down the drive?
+ */
+ if (ata_disk_isssd()) {
+ ATA_OUT8(ATA_COMMAND, CMD_STANDBY_IMMEDIATE);
+ } else {
+ ATA_OUT8(ATA_COMMAND, CMD_SLEEP);
+ }
+
+ if (!wait_for_rdy()) {
DEBUGF("ata_perform_sleep() - CMD failed\n");
return -2;
}
@@ -265,6 +265,34 @@ static int ata_perform_sleep(void)
return 0;
}
+static int ata_perform_flush_cache(void)
+{
+ /* Don't issue the flush cache command if the device
+ doesn't support it, even though it's mandatory.
+ */
+ if (!(identify_info[83] & (1 << 12)))
+ return 0;
+
+ logf("ata FLUSH CACHE %ld", current_tick);
+
+ ATA_OUT8(ATA_SELECT, ata_device);
+
+ if(!wait_for_rdy()) {
+ DEBUGF("ata_perform_flush_cache() - not RDY\n");
+ return -1;
+ }
+
+ ATA_OUT8(ATA_COMMAND, CMD_FLUSH_CACHE);
+
+ if (!wait_for_rdy()) {
+ DEBUGF("ata_perform_flush_cache() - CMD failed\n");
+ return -2;
+ }
+
+ return 0;
+}
+
+
static ICODE_ATTR int wait_for_start_of_transfer(void)
{
if (!wait_for_bsy())
@@ -361,15 +389,43 @@ static ICODE_ATTR void copy_write_sectors(const unsigned char* buf,
int ata_disk_isssd(void)
{
- /* offset 217 is "Nominal Rotation rate"
- 0x0000 == Not reported
- 0x0001 == Solid State
- 0x0401 -> 0xffe == RPM
- All others reserved
+ /*
+ Offset 217 is "Nominal Rotation rate"
+ 0x0000 == Not reported
+ 0x0001 == Solid State
+ 0x0401 -> 0xffe == RPM
+ All others reserved
+
+ Some CF cards return 0x0100 (ie byteswapped 0x0001) so accept either.
+ However, this is a relatively recent change, and we can't rely on it,
+ especially for the FC1307A CF->SD adapters!
+
+ Offset 168 is "Nominal Form Factor"
+ all values >= 0x06 are guaranteed to be Solid State (mSATA, m.2, etc)
+
+ Offset 83 b2 and/or 86 b2 is set to show device implementes CFA commands
- Some CF cards return 0x0100 (ie byteswapped 0x0001) so accept either
+ Offset 169 b0 is set to show device implements TRIM.
+
+ Offset 0 is 0x848a for CF, but that's not guaranteed, because reasons.
*/
- return (identify_info[217] == 0x0001 || identify_info[217] == 0x0100);
+ return (identify_info[83] & (1<<2) || identify_info[86] & (1<<2) ||
+ (identify_info[168] & 0x0f) >= 0x06 ||
+ identify_info[169] & (1<<0) ||
+ identify_info[217] == 0x0001 || identify_info[217] == 0x0100);
+}
+
+int ata_disk_can_poweroff(void)
+{
+ /* Some SSDs don't like getting powered off, presumably because
+ in the real world they're not in removable form factors and
+ don't expect to have power removed.
+
+ In particular, mSATA, m.2, and MicroSSD are suspect.
+ */
+
+ return ((identify_info[168] & 0x0f) < 0x06 ||
+ (identify_info[168] & 0x0f) > 0x08);
}
static int ata_transfer_sectors(unsigned long start,
@@ -836,29 +892,16 @@ void ata_spindown(int seconds)
bool ata_disk_is_active(void)
{
-#ifdef FC1307A_WORKAROUND
- /* "active" == "spinning" in this context.
- without power management this becomes moot */
- if (!(identify_info[82] & (1 << 3)))
- return false;
-#endif
-
return ata_state >= ATA_SPINUP;
}
void ata_sleepnow(void)
{
-#ifdef FC1307A_WORKAROUND
- /* Completely disable all power management */
- if (!(identify_info[82] & (1 << 3)))
- return;
-#endif
-
if (ata_state >= ATA_SPINUP) {
logf("ata SLEEPNOW %ld", current_tick);
mutex_lock(&ata_mtx);
if (ata_state == ATA_ON) {
- if (!ata_perform_sleep()) {
+ if (!ata_perform_flush_cache() && !ata_perform_sleep()) {
ata_state = ATA_SLEEPING;
schedule_ata_power_off();
}
@@ -1093,7 +1136,7 @@ static int set_features(void)
unsigned char parameter;
} features[] = {
{ 83, 14, 0x03, 0 }, /* force PIO mode */
- { 83, 3, 0x05, 0x80 }, /* adv. power management: lowest w/o standby */
+ { 83, 3, 0x05, 0x80 }, /* adv. power management: lowest w/o standby */ // TODO: What about FC1307A that doesn't advertise this properly?
{ 83, 9, 0x42, 0x80 }, /* acoustic management: lowest noise */
{ 82, 6, 0xaa, 0 }, /* enable read look-ahead */
#ifdef HAVE_ATA_DMA
diff --git a/firmware/drivers/ata_flash.c b/firmware/drivers/ata_flash.c
deleted file mode 100644
index fbdd7f04ee..0000000000
--- a/firmware/drivers/ata_flash.c
+++ /dev/null
@@ -1,484 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2005 Tomasz Malesinski
- *
- * 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 "storage.h"
-#include <stdbool.h>
-#include <string.h>
-
-#if CONFIG_CPU == PNX0101
-#include "pnx0101.h"
-#endif
-
-/*
-#include "kernel.h"
-#include "thread.h"
-#include "led.h"
-#include "cpu.h"
-#include "system.h"
-#include "debug.h"
-#include "panic.h"
-#include "usb.h"
-#include "power.h"
-#include "string.h"
-*/
-
-#define SECTOR_SIZE (512)
-
-static long last_disk_activity = -1;
-
-#if CONFIG_FLASH == FLASH_IFP7XX
-static unsigned char flash_ce[4] = {0x20, 0x02, 0x10, 0x08};
-
-#define FLASH_IO_BASE 0x28000000
-#define FLASH_REG_DATA (*((volatile unsigned char*)(FLASH_IO_BASE)))
-#define FLASH_REG_CMD (*((volatile unsigned char*)(FLASH_IO_BASE + 4)))
-#define FLASH_REG_ADDR (*((volatile unsigned char*)(FLASH_IO_BASE + 8)))
-
-#define SEGMENT_SIZE 1000
-#define MAX_N_SEGMENTS 8
-
-#endif
-
-#define FLASH_MODEL_NONE 0
-#define FLASH_MODEL_256 1
-#define FLASH_MODEL_512 2
-
-struct flash_disk
-{
- unsigned short block_map[MAX_N_SEGMENTS][SEGMENT_SIZE];
- short cur_block;
- int cur_phblock_start;
- int n_chips;
- unsigned char chip_no[4];
- unsigned char model;
-};
-
-static struct flash_disk flash_disk;
-
-static void flash_select_chip(int no, int sel)
-{
-#if CONFIG_FLASH == FLASH_IFP7XX
- if (sel)
- GPIO5_CLR = flash_ce[no];
- else
- GPIO5_SET = flash_ce[no];
-#endif
-}
-
-static inline unsigned char flash_read_data(void)
-{
- return FLASH_REG_DATA;
-}
-
-static inline void flash_write_data(unsigned char data)
-{
- FLASH_REG_DATA = data;
-}
-
-/* TODO: these two doesn't work when inlined, probably some
- delay is required */
-
-static void flash_write_cmd(unsigned char cmd)
-{
- FLASH_REG_CMD = cmd;
-}
-
-static void flash_write_addr(unsigned char addr)
-{
- FLASH_REG_ADDR = addr;
-}
-
-static void flash_wait_ready(void)
-{
- int i;
- for (i = 0; i < 5; i++)
- while ((GPIO6_READ & 8) == 0);
-}
-
-static unsigned char model_n_sectors_order[] = {0, 19, 20};
-
-static int flash_map_sector(int sector, int* chip, int* chip_sector)
-{
- int ord, c;
- if (flash_disk.model == FLASH_MODEL_NONE)
- return -1;
-
- ord = model_n_sectors_order[flash_disk.model];
- c = sector >> ord;
- *chip_sector = sector & ((1 << ord) - 1);
-
- if (c >= flash_disk.n_chips)
- return -1;
-
- *chip = flash_disk.chip_no[c];
- return 0;
-}
-
-static int flash_read_id(int no) {
- int id;
-
- flash_select_chip(no, 1);
- flash_write_cmd(0x90);
- flash_write_addr(0);
-
- flash_read_data();
- id = flash_read_data();
-
- flash_select_chip(no, 0);
- return id;
-}
-
-static int flash_read_sector(int sector, unsigned char* buf,
- unsigned char* oob)
-{
- unsigned long *bufl = (unsigned long *)buf;
- int chip, chip_sector;
- int i;
-
- if (flash_map_sector(sector, &chip, &chip_sector) < 0)
- return -1;
-
- flash_select_chip(chip, 1);
-
- flash_write_cmd(0x00);
- flash_write_addr(0);
- flash_write_addr((chip_sector << 1) & 7);
- flash_write_addr((chip_sector >> 2) & 0xff);
- flash_write_addr((chip_sector >> 10) & 0xff);
- flash_write_addr((chip_sector >> 18) & 0xff);
- flash_write_cmd(0x30);
-
- flash_wait_ready();
-
- if ((unsigned long)buf & 3)
- {
- for (i = 0; i < 512; i++)
- buf[i] = flash_read_data();
- }
- else
- {
- for (i = 0; i < 512 / 4; i++) {
- unsigned long v;
-#ifdef ROCKBOX_LITTLE_ENDIAN
- v = flash_read_data();
- v |= (unsigned long)flash_read_data() << 8;
- v |= (unsigned long)flash_read_data() << 16;
- v |= (unsigned long)flash_read_data() << 24;
-#else
- v = (unsigned long)flash_read_data() << 24;
- v |= (unsigned long)flash_read_data() << 16;
- v |= (unsigned long)flash_read_data() << 8;
- v |= flash_read_data();
-#endif
- bufl[i] = v;
- }
- }
-
- flash_write_cmd(0x05);
- flash_write_addr((chip_sector & 3) * 0x10);
- flash_write_addr(8);
- flash_write_cmd(0xe0);
-
- for (i = 0; i < 16; i++)
- oob[i] = flash_read_data();
-
- flash_select_chip(chip, 0);
- return 0;
-}
-
-static int flash_read_sector_oob(int sector, unsigned char* oob)
-{
- int chip, chip_sector;
- int i;
-
- if (flash_map_sector(sector, &chip, &chip_sector) < 0)
- return -1;
-
- flash_select_chip(chip, 1);
-
- flash_write_cmd(0x00);
- flash_write_addr((chip_sector & 3) * 0x10);
- flash_write_addr(8);
- flash_write_addr((chip_sector >> 2) & 0xff);
- flash_write_addr((chip_sector >> 10) & 0xff);
- flash_write_addr((chip_sector >> 18) & 0xff);
- flash_write_cmd(0x30);
-
- flash_wait_ready();
-
- for (i = 0; i < 16; i++)
- oob[i] = flash_read_data();
-
- flash_select_chip(chip, 0);
- return 0;
-}
-
-static unsigned char model_n_segments[] = {0, 2, 4};
-
-static inline int flash_get_n_segments(void)
-{
- return model_n_segments[flash_disk.model] * flash_disk.n_chips;
-}
-
-static inline int flash_get_n_phblocks(void)
-{
- return 1024;
-}
-
-static int model_n_sectors_in_block[] = {0, 256, 256};
-
-static int flash_get_n_sectors_in_block(void)
-{
- return model_n_sectors_in_block[flash_disk.model];
-}
-
-static int flash_phblock_to_sector(int segment, int block)
-{
- return (segment * flash_get_n_phblocks() + block)
- * flash_get_n_sectors_in_block();
-}
-
-static int flash_is_bad_block(unsigned char* oob)
-{
- /* TODO: should we check two pages? (see datasheet) */
- return oob[0] != 0xff;
-}
-
-static int count_1(int n) {
- int r = 0;
- while (n != 0) {
- r += (n & 1);
- n >>= 1;
- }
- return r;
-}
-
-static int flash_get_logical_block_no(unsigned char* oob)
-{
- int no1, no2;
- no1 = oob[6] + (oob[7] << 8);
- no2 = oob[11] + (oob[12] << 8);
-
- if (no1 == no2 && (no1 & 0xf000) == 0x1000)
- return (no1 & 0xfff) >> 1;
-
- if (count_1(no1 ^ no2) > 1)
- return -1;
-
- if ((no1 & 0xf000) == 0x1000
- && (count_1(no1) & 1) == 0)
- return (no1 & 0xfff) >> 1;
-
- if ((no2 & 0xf000) == 0x1000
- && (count_1(no2) & 1) == 0)
- return (no2 & 0xfff) >> 1;
-
- return -1;
-}
-
-static int flash_disk_scan(void)
-{
- int n_segments, n_phblocks;
- unsigned char oob[16];
- int s, b;
-
- /* TODO: checking for double blocks */
-
- n_segments = flash_get_n_segments();
- n_phblocks = flash_get_n_phblocks();
-
- flash_disk.cur_block = -1;
- flash_disk.cur_phblock_start = -1;
-
- for (s = 0; s < n_segments; s++)
- {
- for (b = 0; b < n_phblocks; b++)
- {
- int r;
- r = flash_read_sector_oob(flash_phblock_to_sector(s, b),
- oob);
- if (r >= 0 && !flash_is_bad_block(oob))
- {
- int lb;
- lb = flash_get_logical_block_no(oob);
- if (lb >= 0 && lb < SEGMENT_SIZE)
- flash_disk.block_map[s][lb] = b;
- }
- }
- }
- return 0;
-}
-
-static int flash_disk_find_block(int block)
-{
- int seg, bmod, phb;
- unsigned char oob[16];
- int r;
-
- if (block >= SEGMENT_SIZE * flash_get_n_segments())
- return -1;
-
- if (block == flash_disk.cur_block)
- return flash_disk.cur_phblock_start;
-
- seg = block / SEGMENT_SIZE;
- bmod = block % SEGMENT_SIZE;
-
- phb = flash_disk.block_map[seg][bmod];
- r = flash_read_sector_oob(flash_phblock_to_sector(seg, phb), oob);
- if (r < 0)
- return -1;
- if (flash_is_bad_block(oob))
- return -1;
- if (flash_get_logical_block_no(oob) != bmod)
- return -1;
-
- flash_disk.cur_block = block;
- flash_disk.cur_phblock_start = flash_phblock_to_sector(seg, phb);
- return flash_disk.cur_phblock_start;
-}
-
-static int flash_disk_read_sectors(unsigned long start,
- int count,
- void* buf)
-{
- int block, secmod, done;
- int phb;
- char oob[16];
-
- block = start / flash_get_n_sectors_in_block();
- secmod = start % flash_get_n_sectors_in_block();
-
- phb = flash_disk_find_block(block);
- done = 0;
- while (count > 0 && secmod < flash_get_n_sectors_in_block())
- {
- if (phb >= 0)
- flash_read_sector(phb + secmod, buf, oob);
- else
- memset(buf, 0, SECTOR_SIZE);
-
- buf += SECTOR_SIZE;
- count--;
- secmod++;
- done++;
- }
- return done;
-}
-
-int nand_read_sectors(IF_MD(int drive,)
- unsigned long start,
- int incount,
- void* inbuf)
-{
- while (incount > 0)
- {
- int done = flash_disk_read_sectors(start, incount, inbuf);
- if (done < 0)
- return -1;
- start += done;
- incount -= done;
- inbuf += SECTOR_SIZE * done;
- }
- return 0;
-}
-
-int nand_write_sectors(IF_MD(int drive,)
- unsigned long start,
- int count,
- const void* buf)
-{
- (void)start;
- (void)count;
- (void)buf;
- return -1;
-}
-
-int nand_init(void)
-{
- int i, id, id2;
-
- id = flash_read_id(0);
- switch (id)
- {
- case 0xda:
- flash_disk.model = FLASH_MODEL_256;
- break;
- case 0xdc:
- flash_disk.model = FLASH_MODEL_512;
- break;
- default:
- flash_disk.model = FLASH_MODEL_NONE;
- return -1;
- }
-
- flash_disk.n_chips = 1;
- flash_disk.chip_no[0] = 0;
- for (i = 1; i < 4; i++)
- {
- id2 = flash_read_id(i);
- if (id2 == id)
- flash_disk.chip_no[flash_disk.n_chips++] = i;
- }
-
- if (flash_disk_scan() < 0)
- return -2;
-
- return 0;
-}
-
-long nand_last_disk_activity(void)
-{
- return last_disk_activity;
-}
-
-#ifdef STORAGE_GET_INFO
-void nand_get_info(struct storage_info *info)
-{
- unsigned long blocks;
- int i;
-
- /* firmware version */
- info->revision="0.00";
-
- /* vendor field, need better name? */
- info->vendor="Rockbox";
- /* model field, need better name? */
- info->product="TNFL";
-
- /* blocks count */
- info->num_sectors = 0;
- info->sector_size=SECTOR_SIZE;
-
- info->serial=0;
-}
-#endif
-
-#ifdef CONFIG_STORAGE_MULTI
-int nand_num_drives(int first_drive)
-{
- /* We don't care which logical drive number(s) we have been assigned */
- (void)first_drive;
-
- return 1;
-}
-#endif
-
diff --git a/firmware/drivers/audio/aic3x.c b/firmware/drivers/audio/aic3x.c
index 9b705db7f5..39576e9f47 100644
--- a/firmware/drivers/audio/aic3x.c
+++ b/firmware/drivers/audio/aic3x.c
@@ -305,8 +305,8 @@ void aic3x_switch_output(bool stereo)
{
if (stereo)
{
- /* mute MONO_LOP/M */
- aic3x_change_reg(AIC3X_MONO_LOP_M_LVL, 0x00, 0xF6);
+ /* MONO_LOP/M not fully powered up */
+ aic3x_change_reg(AIC3X_MONO_LOP_M_LVL, 0x00, 0xFE);
/* HPLOUT fully powered up */
aic3x_change_reg(AIC3X_HPLOUT_LVL, 0x01, 0xFF);
/* HPROUT fully powered up */
@@ -316,8 +316,8 @@ void aic3x_switch_output(bool stereo)
}
else
{
- /* MONO_LOP/M not muted */
- aic3x_change_reg(AIC3X_MONO_LOP_M_LVL, 0x09, 0xFF);
+ /* MONO_LOP/M fully powered up */
+ aic3x_change_reg(AIC3X_MONO_LOP_M_LVL, 0x01, 0xFF);
/* HPLOUT not fully powered up */
aic3x_change_reg(AIC3X_HPLOUT_LVL, 0x00, 0xFE);
/* HPROUT not fully powered up */
diff --git a/firmware/drivers/audio/ak4376.c b/firmware/drivers/audio/ak4376.c
index 494bbabfa4..8d2b9f44f3 100644
--- a/firmware/drivers/audio/ak4376.c
+++ b/firmware/drivers/audio/ak4376.c
@@ -27,6 +27,18 @@
#include "system.h"
#include "i2c-async.h"
+/* sample rates supported by the hardware */
+#define CAPS (SAMPR_CAP_192 | SAMPR_CAP_176 | \
+ SAMPR_CAP_96 | SAMPR_CAP_88 | SAMPR_CAP_64 | \
+ SAMPR_CAP_48 | SAMPR_CAP_44 | SAMPR_CAP_32 | \
+ SAMPR_CAP_24 | SAMPR_CAP_22 | SAMPR_CAP_16 | \
+ SAMPR_CAP_12 | SAMPR_CAP_11 | SAMPR_CAP_8)
+
+/* future proofing */
+#if (HW_SAMPR_CAPS & ~CAPS) != 0
+# error "incorrect HW_SAMPR_CAPS"
+#endif
+
#ifndef HAVE_SW_VOLUME_CONTROL
# error "AK4376 requires HAVE_SW_VOLUME_CONTROL!"
#endif
@@ -40,7 +52,7 @@
*/
/* Converts HW_FREQ_XX constants to register values */
-static const int ak4376_fsel_to_hw[] = {
+static const uint8_t ak4376_fsel_to_hw[] = {
HW_HAVE_192_(AK4376_FS_192,)
HW_HAVE_176_(AK4376_FS_176,)
HW_HAVE_96_(AK4376_FS_96,)
@@ -57,19 +69,13 @@ static const int ak4376_fsel_to_hw[] = {
HW_HAVE_8_(AK4376_FS_8,)
};
-static struct ak4376 {
- int fsel;
- int low_mode;
- int regs[AK4376_NUM_REGS];
-} ak4376;
+static int ak4376_regs[AK4376_NUM_REGS];
-void ak4376_init(void)
+void ak4376_open(void)
{
/* Initialize DAC state */
- ak4376.fsel = HW_FREQ_48;
- ak4376.low_mode = 0;
for(int i = 0; i < AK4376_NUM_REGS; ++i)
- ak4376.regs[i] = -1;
+ ak4376_regs[i] = -1;
/* Initial reset after power-on */
ak4376_set_pdn_pin(0);
@@ -77,11 +83,11 @@ void ak4376_init(void)
ak4376_set_pdn_pin(1);
mdelay(1);
- static const int init_config[] = {
+ static const uint8_t init_config[] = {
/* Ensure HPRHZ, HPLHZ are 0 */
AK4376_REG_OUTPUT_MODE, 0x00,
/* Mute all volume controls */
- AK4376_REG_MIXER, 0x00,
+ AK4376_REG_MIXER, AK4376_MIX_LCH | (AK4376_MIX_RCH << 4),
AK4376_REG_LCH_VOLUME, 0x80,
AK4376_REG_RCH_VOLUME, 0x00,
AK4376_REG_AMP_VOLUME, 0x00,
@@ -102,9 +108,6 @@ void ak4376_init(void)
/* Write initial configuration prior to power-up */
for(size_t i = 0; i < ARRAYLEN(init_config); i += 2)
ak4376_write(init_config[i], init_config[i+1]);
-
- /* Initial frequency setting, also handles DAC/amp power-up */
- audiohw_set_frequency(HW_FREQ_48);
}
void ak4376_close(void)
@@ -121,22 +124,22 @@ void ak4376_close(void)
void ak4376_write(int reg, int value)
{
/* Ensure value is sensible and differs from the last set value */
- if((value & 0xff) == value && ak4376.regs[reg] != value) {
+ if((value & 0xff) == value && ak4376_regs[reg] != value) {
int r = i2c_reg_write1(AK4376_BUS, AK4376_ADDR, reg, value);
if(r == I2C_STATUS_OK)
- ak4376.regs[reg] = value;
+ ak4376_regs[reg] = value;
else
- ak4376.regs[reg] = -1;
+ ak4376_regs[reg] = -1;
}
}
int ak4376_read(int reg)
{
/* Only read from I2C if we don't already know the value */
- if(ak4376.regs[reg] < 0)
- ak4376.regs[reg] = i2c_reg_read1(AK4376_BUS, AK4376_ADDR, reg);
+ if(ak4376_regs[reg] < 0)
+ ak4376_regs[reg] = i2c_reg_read1(AK4376_BUS, AK4376_ADDR, reg);
- return ak4376.regs[reg];
+ return ak4376_regs[reg];
}
static int round_step_up(int x, int step)
@@ -147,14 +150,9 @@ static int round_step_up(int x, int step)
return x - rem;
}
-static void calc_volumes(int vol, int* mix, int* dig, int* sw)
+static void calc_volumes(int vol, int* dig, int* sw)
{
- /* Mixer can divide by 2, which gives an extra -6 dB adjustment */
- if(vol < AK4376_DIG_VOLUME_MIN) {
- *mix |= AK4376_MIX_HALF;
- vol += 60;
- }
-
+ /* Apply digital volume */
*dig = round_step_up(vol, AK4376_DIG_VOLUME_STEP);
*dig = MIN(*dig, AK4376_DIG_VOLUME_MAX);
*dig = MAX(*dig, AK4376_DIG_VOLUME_MIN);
@@ -180,11 +178,11 @@ static int amp_vol_to_hw(int vol)
return (vol - AK4376_AMP_VOLUME_MIN) / AK4376_AMP_VOLUME_STEP + 1;
}
-void audiohw_set_volume(int vol_l, int vol_r)
+void ak4376_set_volume(int vol_l, int vol_r)
{
int amp;
- int mix_l = AK4376_MIX_LCH, dig_l, sw_l;
- int mix_r = AK4376_MIX_RCH, dig_r, sw_r;
+ int dig_l, sw_l;
+ int dig_r, sw_r;
if(vol_l <= AK4376_MIN_VOLUME && vol_r <= AK4376_MIN_VOLUME) {
/* Special case for full mute */
@@ -199,18 +197,17 @@ void audiohw_set_volume(int vol_l, int vol_r)
amp = MAX(amp, AK4376_AMP_VOLUME_MIN);
/* Other controls are stereo */
- calc_volumes(vol_l - amp, &mix_l, &dig_l, &sw_l);
- calc_volumes(vol_r - amp, &mix_r, &dig_r, &sw_r);
+ calc_volumes(vol_l - amp, &dig_l, &sw_l);
+ calc_volumes(vol_r - amp, &dig_r, &sw_r);
}
- ak4376_write(AK4376_REG_MIXER, (mix_l & 0xf) | ((mix_r & 0xf) << 4));
ak4376_write(AK4376_REG_LCH_VOLUME, dig_vol_to_hw(dig_l) | (1 << 7));
ak4376_write(AK4376_REG_RCH_VOLUME, dig_vol_to_hw(dig_r));
ak4376_write(AK4376_REG_AMP_VOLUME, amp_vol_to_hw(amp));
pcm_set_master_volume(sw_l, sw_r);
}
-void audiohw_set_filter_roll_off(int val)
+void ak4376_set_filter_roll_off(int val)
{
int reg = ak4376_read(AK4376_REG_FILTER);
reg &= ~0xc0;
@@ -218,11 +215,8 @@ void audiohw_set_filter_roll_off(int val)
ak4376_write(AK4376_REG_FILTER, reg);
}
-void audiohw_set_frequency(int fsel)
+void ak4376_set_freqmode(int fsel, int mult, int power_mode)
{
- /* Determine master clock multiplier */
- int mult = ak4376_set_mclk_freq(fsel, false);
-
/* Calculate clock mode for frequency. Multipliers of 32/64 are only
* for rates >= 256 KHz which are not supported by Rockbox, so they
* are commented out -- but they're in the correct place. */
@@ -248,27 +242,29 @@ void audiohw_set_frequency(int fsel)
/* Handle the DSMLP bit in the MODE_CTRL register */
int mode_ctrl = 0x00;
- if(ak4376.low_mode || hw_freq_sampr[fsel] <= SAMPR_12)
+ if(power_mode == SOUND_LOW_POWER || hw_freq_sampr[fsel] <= SAMPR_12)
mode_ctrl |= 0x40;
+ /* Handle the LPMODE bit */
+ int pwr3 = power_mode == SOUND_LOW_POWER ? 0x11 : 0x01;
+
+ /* The datasheet says the HP amp must be powered down before changing
+ * the operating mode of the DAC or HP amp. I'm assuming this means
+ * the amp must be shut down when changing DSMLP or LPMODE. */
+ int cur_mode_ctrl = ak4376_read(AK4376_REG_MODE_CTRL);
+ int cur_pwr3 = ak4376_read(AK4376_REG_PWR3);
+ bool disable_amp = mode_ctrl != cur_mode_ctrl || pwr3 != cur_pwr3;
+
/* Program the new settings */
+ if(disable_amp)
+ ak4376_write(AK4376_REG_PWR4, 0x00);
+
ak4376_write(AK4376_REG_CLOCK_MODE, clock_mode);
ak4376_write(AK4376_REG_MODE_CTRL, mode_ctrl);
- ak4376_write(AK4376_REG_PWR3, ak4376.low_mode ? 0x11 : 0x01);
-
- /* Enable the master clock */
- ak4376_set_mclk_freq(fsel, true);
+ ak4376_write(AK4376_REG_PWR3, pwr3);
- /* Remember the frequency */
- ak4376.fsel = fsel;
-}
-
-void audiohw_set_power_mode(int mode)
-{
- /* This is handled via audiohw_set_frequency() since changing LPMODE
- * bit requires power-down/power-up & changing other bits as well */
- if(ak4376.low_mode != mode) {
- ak4376.low_mode = mode;
- audiohw_set_frequency(ak4376.fsel);
+ if(disable_amp) {
+ ak4376_write(AK4376_REG_PWR4, 0x03);
+ mdelay(26);
}
}
diff --git a/firmware/target/arm/pnx0101/iriver-ifp7xx/power-ifp7xx.c b/firmware/drivers/audio/eros_qn_codec.c
index 1fd9d68028..095b3b5305 100644
--- a/firmware/target/arm/pnx0101/iriver-ifp7xx/power-ifp7xx.c
+++ b/firmware/drivers/audio/eros_qn_codec.c
@@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Copyright (C) 2002 by Linus Nielsen Feltzing
+ * Copyright (C) 2021 Andrew Ryabinin, Dana Conrad
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -18,59 +18,45 @@
* KIND, either express or implied.
*
****************************************************************************/
-#include "config.h"
-#include "cpu.h"
-#include <stdbool.h>
-#include "kernel.h"
-#include "system.h"
-#include "power.h"
-#include "logf.h"
-#include "usb.h"
-
-#if CONFIG_TUNER
-
-bool tuner_power(bool status)
-{
- (void)status;
- return true;
-}
-#endif /* #if CONFIG_TUNER */
-
-#ifndef SIMULATOR
+#include "system.h"
+#include "eros_qn_codec.h"
+#include "config.h"
+#include "audio.h"
+#include "audiohw.h"
+#include "settings.h"
+#include "pcm_sw_volume.h"
-void power_init(void)
-{
-}
+#include "gpio-x1000.h"
-void ide_power_enable(bool on)
-{
- (void)on;
- /* no ide controller */
-}
+static long int vol_l_hw = PCM5102A_VOLUME_MIN;
+static long int vol_r_hw = PCM5102A_VOLUME_MIN;
+int es9018k2m_present_flag = 0;
-bool ide_powered(void)
+void eros_qn_set_outputs(void)
{
- return true; /* pretend always powered if not controlable */
+ audiohw_set_volume(vol_l_hw, vol_r_hw);
}
-void power_off(void)
+void eros_qn_set_last_vol(long int vol_l, long int vol_r)
{
- disable_interrupt(IRQ_FIQ_STATUS);
- GPIO1_CLR = 1 << 16;
- GPIO2_SET = 1;
- while(1);
+ vol_l_hw = vol_l;
+ vol_r_hw = vol_r;
}
-#else
-
-void power_off(void)
+int eros_qn_get_volume_limit(void)
{
+ return (global_settings.volume_limit * 10);
}
-void ide_power_enable(bool on)
+void eros_qn_switch_output(int select)
{
- (void)on;
-}
-
-#endif /* SIMULATOR */
+ if (select == 0)
+ {
+ gpio_set_level(GPIO_STEREOSW_SEL, 0);
+ }
+ else
+ {
+ gpio_set_level(GPIO_STEREOSW_SEL, 1);
+ }
+} \ No newline at end of file
diff --git a/firmware/drivers/audio/erosqlinux_codec.c b/firmware/drivers/audio/erosqlinux_codec.c
index abb4fea01f..02c35e3c00 100644
--- a/firmware/drivers/audio/erosqlinux_codec.c
+++ b/firmware/drivers/audio/erosqlinux_codec.c
@@ -25,15 +25,11 @@
#include "config.h"
#include "audio.h"
#include "audiohw.h"
-#include "button.h"
#include "system.h"
-#include "kernel.h"
#include "panic.h"
#include "sysfs.h"
#include "alsa-controls.h"
#include "pcm-alsa.h"
-#include "pcm_sw_volume.h"
-
#include "settings.h"
#include "logf.h"
@@ -64,32 +60,19 @@
: values=4
*/
-static int fd_hw = -1;
+static int hw_init = 0;
static long int vol_l_hw = 255;
static long int vol_r_hw = 255;
static long int last_ps = -1;
-static void hw_open(void)
-{
- fd_hw = open("/dev/snd/controlC0", O_RDWR);
- if(fd_hw < 0)
- panicf("Cannot open '/dev/snd/controlC0'");
-}
-
-static void hw_close(void)
-{
- close(fd_hw);
- fd_hw = -1;
-}
-
static int muted = -1;
void audiohw_mute(int mute)
{
logf("mute %d", mute);
- if (fd_hw < 0 || muted == mute)
+ if (hw_init < 0 || muted == mute)
return;
muted = mute;
@@ -111,7 +94,7 @@ int erosq_get_outputs(void) {
int status = 0;
- if (fd_hw < 0) return ps;
+ if (!hw_init) return ps;
const char * const sysfs_lo_switch = "/sys/class/switch/lineout/state";
const char * const sysfs_hs_switch = "/sys/class/switch/headset/state";
@@ -129,7 +112,7 @@ int erosq_get_outputs(void) {
void erosq_set_output(int ps)
{
- if (fd_hw < 0 || muted) return;
+ if (!hw_init || muted) return;
if (last_ps != ps)
{
@@ -145,7 +128,7 @@ void audiohw_preinit(void)
{
logf("hw preinit");
alsa_controls_init("default");
- hw_open();
+ hw_init = 1;
audiohw_mute(false); /* No need to stay muted */
}
@@ -157,7 +140,8 @@ void audiohw_postinit(void)
void audiohw_close(void)
{
logf("hw close");
- hw_close();
+ hw_init = 0;
+ muted = -1;
alsa_controls_close();
}
@@ -167,7 +151,7 @@ void audiohw_set_frequency(int fsel)
}
/* min/max for pcm volume */
-const int min_pcm = -430;
+const int min_pcm = -740;
const int max_pcm = 0;
void audiohw_set_volume(int vol_l, int vol_r)
@@ -185,7 +169,7 @@ void audiohw_set_volume(int vol_l, int vol_r)
output gain, we have to back off on the PCM signal
to avoid blowing out the signal.
*/
- l = r = global_settings.volume_limit;
+ l = r = global_settings.volume_limit * 10;
} else {
l = vol_l_hw;
r = vol_r_hw;
@@ -193,7 +177,7 @@ void audiohw_set_volume(int vol_l, int vol_r)
int sw_volume_l = l <= min_pcm ? min_pcm : MIN(l, max_pcm);
int sw_volume_r = r <= min_pcm ? min_pcm : MIN(r, max_pcm);
- pcm_set_mixer_volume(sw_volume_l / 10, sw_volume_r / 10);
+ pcm_set_mixer_volume(sw_volume_l / 20, sw_volume_r / 20);
}
void audiohw_set_lineout_volume(int vol_l, int vol_r)
@@ -206,7 +190,7 @@ void audiohw_set_lineout_volume(int vol_l, int vol_r)
(void)vol_r;
if (lineout_inserted()) {
- l = r = global_settings.volume_limit;
+ l = r = global_settings.volume_limit * 10;
} else {
l = vol_l_hw;
r = vol_r_hw;
@@ -214,5 +198,5 @@ void audiohw_set_lineout_volume(int vol_l, int vol_r)
int sw_volume_l = l <= min_pcm ? min_pcm : MIN(l, max_pcm);
int sw_volume_r = r <= min_pcm ? min_pcm : MIN(r, max_pcm);
- pcm_set_mixer_volume(sw_volume_l / 10, sw_volume_r / 10);
+ pcm_set_mixer_volume(sw_volume_l / 20, sw_volume_r / 20);
}
diff --git a/firmware/drivers/audio/es9018.c b/firmware/drivers/audio/es9018.c
index 89e8c1d46f..6a73f7a2d3 100644
--- a/firmware/drivers/audio/es9018.c
+++ b/firmware/drivers/audio/es9018.c
@@ -25,8 +25,8 @@
#include "audio.h"
#include "audiohw.h"
-/* NOTE: The register names are not known, as the register numbering
- listed in the ES9018 datasheet does not match what is described below.. */
+/* NOTE: This implementation is specifically for the ES9018K2M, which has a different register
+ * structure from the ES9018. */
static uint8_t reg0 = 0x00; /* System settings. Default value of register 0 */
static uint8_t reg1 = 0x80; /* Input settings. Manual input, I2S, 32-bit (?) */
diff --git a/firmware/drivers/audio/es9018k2m.c b/firmware/drivers/audio/es9018k2m.c
new file mode 100644
index 0000000000..3f14aaea39
--- /dev/null
+++ b/firmware/drivers/audio/es9018k2m.c
@@ -0,0 +1,161 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ *
+ * Copyright (c) 2023 Dana Conrad
+ *
+ * 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 "es9018k2m.h"
+#include "i2c-async.h"
+#include "action.h"
+
+//======================================================================================
+// ES9018K2M support stuff
+
+#ifndef ES9018K2M_BUS
+# error "No definition for ES9018K2M I2C bus!"
+#endif
+
+#ifndef ES9018K2M_ADDR
+# error "No definition for ES9018K2M I2C address!"
+#endif
+
+static int vol_tenthdb2hw(const int tdb)
+{
+ if (tdb < ES9018K2M_VOLUME_MIN) {
+ return 0xff;
+ } else if (tdb > ES9018K2M_VOLUME_MAX) {
+ return 0x00;
+ } else {
+ return (-tdb/5);
+ }
+}
+
+/* NOTE: This implementation is for the ES9018K2M specifically. */
+/* Register defaults from the datasheet. */
+/* These are basically just for reference. All defaults work out for us.
+ * static uint8_t reg0_system_settings = 0x00; // System settings. Default value of register 0
+ * static uint8_t reg1_input_configuration = 0x8C; // Input settings. I2S input, 32-bit
+ * static uint8_t reg4_automute_time = 0x00; // Automute time. Default = disabled
+ * static uint8_t reg5_automute_level = 0x68; // Automute level. Default is some level
+ * static uint8_t reg6_deemphasis = 0x4A; // Deemphasis. Default = disabled
+ */
+static uint8_t reg7_general_settings = 0x80; // General settings. Default sharp fir, pcm iir and unmuted
+/*
+ * static uint8_t reg8_gpio_configuration = 0x10; // GPIO configuration
+ * static uint8_t reg10_master_mode_control = 0x05; // Master Mode Control. Default value: master mode off
+ * static uint8_t reg11_channel_mapping = 0x02; // Channel Mapping. Default stereo is Ch1=left, Ch2=right
+ * static uint8_t reg12_dpll_settings = 0x5A; // DPLL Settings. Default = 5 for I2S, A for DSD
+ * static uint8_t reg13_thd_comp = 0x40; // THD Compensation
+ * static uint8_t reg14_softstart_settings = 0x8A; // Soft Start Settings
+ */
+static uint8_t reg21_gpio_input_selection = 0x00; // Oversampling filter. Default: oversampling ON
+
+#define bitSet(value, bit) ((value) |= (1UL << (bit)))
+#define bitClear(value, bit) ((value) &= ~(1UL << (bit)))
+
+static uint8_t vol_reg_l = ES9018K2M_REG15_VOLUME_L;
+static uint8_t reg15_vol_l = 0;
+i2c_descriptor vol_desc_l = {
+ .slave_addr = ES9018K2M_ADDR,
+ .bus_cond = I2C_START | I2C_STOP,
+ .tran_mode = I2C_WRITE,
+ .buffer[0] = &vol_reg_l,
+ .count[0] = 1,
+ .buffer[1] = &reg15_vol_l,
+ .count[1] = 1,
+ .callback = NULL,
+ .arg = 0,
+ .next = NULL,
+};
+
+static uint8_t vol_reg_r = ES9018K2M_REG16_VOLUME_R;
+static uint8_t reg16_vol_r = 0;
+i2c_descriptor vol_desc_r = {
+ .slave_addr = ES9018K2M_ADDR,
+ .bus_cond = I2C_START | I2C_STOP,
+ .tran_mode = I2C_WRITE,
+ .buffer[0] = &vol_reg_r,
+ .count[0] = 1,
+ .buffer[1] = &reg16_vol_r,
+ .count[1] = 1,
+ .callback = NULL,
+ .arg = 0,
+ .next = NULL,
+};
+
+void es9018k2m_set_volume_async(int vol_l, int vol_r)
+{
+ /* Queue writes to the DAC's volume.
+ * Note that this needs to be done asynchronously. From testing, calling
+ * synchronous writes from HP/LO detect causes hangs. */
+ reg15_vol_l = vol_tenthdb2hw(vol_l);
+ reg16_vol_r = vol_tenthdb2hw(vol_r);
+ i2c_async_queue(ES9018K2M_BUS, TIMEOUT_NOBLOCK, I2C_Q_ADD, 0, &vol_desc_l);
+ i2c_async_queue(ES9018K2M_BUS, TIMEOUT_NOBLOCK, I2C_Q_ADD, 0, &vol_desc_r);
+}
+
+void es9018k2m_set_filter_roll_off(int value)
+{
+ /* Note: the ihifi800 implementation manipulates
+ * bit 0 of reg21, but I think that is incorrect?
+ * Bit 2 is the bypass for the IIR digital filters,
+ * Whereas Bit 0 is the oversampling filter, which
+ * the datasheet seems to say should be left on. */
+
+ /* 0 = "Sharp" / Fast Rolloff (Default)
+ 1 = Slow Rolloff
+ 2 = "Short" / Minimum Phase
+ 3 = Bypass */
+ switch(value)
+ {
+ case 0:
+ bitClear(reg7_general_settings, 5);
+ bitClear(reg7_general_settings, 6);
+ bitClear(reg21_gpio_input_selection, 2);
+ break;
+ case 1:
+ bitSet(reg7_general_settings, 5);
+ bitClear(reg7_general_settings, 6);
+ bitClear(reg21_gpio_input_selection, 2);
+ break;
+ case 2:
+ bitClear(reg7_general_settings, 5);
+ bitSet(reg7_general_settings, 6);
+ bitClear(reg21_gpio_input_selection, 2);
+ break;
+ case 3:
+ bitSet(reg21_gpio_input_selection, 2);
+ break;
+ }
+ es9018k2m_write_reg(ES9018K2M_REG7_GENERAL_SETTINGS, reg7_general_settings);
+ es9018k2m_write_reg(ES9018K2M_REG21_GPIO_INPUT_SELECT, reg21_gpio_input_selection);
+}
+
+/* returns I2C_STATUS_OK upon success, I2C_STATUS_* errors upon error */
+int es9018k2m_write_reg(uint8_t reg, uint8_t val)
+{
+ return i2c_reg_write1(ES9018K2M_BUS, ES9018K2M_ADDR, reg, val);
+}
+
+/* returns register value, or -1 upon error */
+int es9018k2m_read_reg(uint8_t reg)
+{
+ return i2c_reg_read1(ES9018K2M_BUS, ES9018K2M_ADDR, reg);
+} \ No newline at end of file
diff --git a/firmware/drivers/audio/es9218.c b/firmware/drivers/audio/es9218.c
new file mode 100644
index 0000000000..76d387221a
--- /dev/null
+++ b/firmware/drivers/audio/es9218.c
@@ -0,0 +1,226 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 "audiohw.h"
+#include "system.h"
+#include "i2c-async.h"
+
+struct es9218_state {
+ enum es9218_clock_gear clk_gear;
+ uint32_t fsr;
+ uint32_t nco;
+};
+
+static struct es9218_state es9218;
+
+void es9218_open(void)
+{
+ /* Enable power supply */
+ es9218_set_power_pin(1);
+
+ /* "Wiggle" reset pin to get the internal oscillator to stabilize.
+ * This should also work if using an external powered oscillator,
+ * although in that case it's unnecessary to do this dance. */
+ es9218_set_reset_pin(1);
+ udelay(75);
+ es9218_set_reset_pin(0);
+ udelay(50);
+ es9218_set_reset_pin(1);
+ mdelay(2);
+
+ /* Initialize driver state */
+ es9218.clk_gear = ES9218_CLK_GEAR_1;
+ es9218.fsr = 0;
+ es9218.nco = 0;
+}
+
+void es9218_close(void)
+{
+ /* Turn off power supply */
+ es9218_set_power_pin(0);
+ es9218_set_reset_pin(0);
+}
+
+static void recalc_nco(void)
+{
+ /* nco * CLK *
+ * fsr = --------- *
+ * 2**32 */
+
+ uint32_t clk = es9218_get_mclk();
+ clk >>= (int)es9218.clk_gear;
+
+ uint64_t nco64 = es9218.fsr;
+ nco64 <<= 32;
+ nco64 /= clk;
+
+ /* let's just ignore overflow... */
+ uint32_t nco = nco64;
+ if(nco != es9218.nco) {
+ es9218.nco = nco;
+
+ /* registers must be written in this order */
+ es9218_write(ES9218_REG_PROG_NCO_BIT0_7, (nco >> 0) & 0xff);
+ es9218_write(ES9218_REG_PROG_NCO_BIT8_15, (nco >> 8) & 0xff);
+ es9218_write(ES9218_REG_PROG_NCO_BIT16_23, (nco >> 16) & 0xff);
+ es9218_write(ES9218_REG_PROG_NCO_BIT24_31, (nco >> 24) & 0xff);
+ }
+}
+
+void es9218_set_clock_gear(enum es9218_clock_gear gear)
+{
+ if(gear != es9218.clk_gear) {
+ es9218.clk_gear = gear;
+ es9218_update(ES9218_REG_SYSTEM, 0x0c, (uint8_t)(gear & 3) << 2);
+ recalc_nco();
+ }
+}
+
+void es9218_set_nco_frequency(uint32_t fsr)
+{
+ if(fsr != es9218.fsr) {
+ es9218.fsr = fsr;
+ recalc_nco();
+ }
+}
+
+void es9218_recompute_nco(void)
+{
+ recalc_nco();
+}
+
+void es9218_set_amp_mode(enum es9218_amp_mode mode)
+{
+ es9218_update(ES9218_REG_AMP_CONFIG, 0x03, (uint8_t)mode & 3);
+}
+
+void es9218_set_amp_powered(bool en)
+{
+ /* this doesn't seem to be necessary..? */
+ es9218_update(ES9218_REG_ANALOG_CTRL, 0x40, en ? 0x40 : 0x00);
+}
+
+void es9218_set_iface_role(enum es9218_iface_role role)
+{
+ /* asrc is used to lock onto the incoming audio frequency and is
+ * only used in aysnchronous slave mode. In synchronous operation,
+ * including master mode, it can be disabled to save power. */
+ int asrc_en = (role == ES9218_IFACE_ROLE_SLAVE ? 1 : 0);
+ int master_mode = (role == ES9218_IFACE_ROLE_MASTER ? 1 : 0);
+
+ es9218_update(ES9218_REG_MASTER_MODE_CONFIG, 1 << 7, master_mode << 7);
+ es9218_update(ES9218_REG_GENERAL_CONFIG, 1 << 7, asrc_en << 7);
+}
+
+void es9218_set_iface_format(enum es9218_iface_format fmt,
+ enum es9218_iface_bits bits)
+{
+ uint8_t val = 0;
+ val |= ((uint8_t)bits & 3) << 6;
+ val |= ((uint8_t)fmt & 3) << 4;
+ /* keep low 4 bits zero -> use normal I2S mode, disable DSD mode */
+ es9218_write(ES9218_REG_INPUT_SEL, val);
+}
+
+static int dig_vol_to_hw(int x)
+{
+ x = MIN(x, ES9218_DIG_VOLUME_MAX);
+ x = MAX(x, ES9218_DIG_VOLUME_MIN);
+ return 0xff - (x - ES9218_DIG_VOLUME_MIN) / ES9218_DIG_VOLUME_STEP;
+}
+
+static int amp_vol_to_hw(int x)
+{
+ x = MIN(x, ES9218_AMP_VOLUME_MAX);
+ x = MAX(x, ES9218_AMP_VOLUME_MIN);
+ return 24 - (x - ES9218_AMP_VOLUME_MIN) / ES9218_AMP_VOLUME_STEP;
+}
+
+void es9218_set_dig_volume(int vol_l, int vol_r)
+{
+ es9218_write(ES9218_REG_VOLUME_LEFT, dig_vol_to_hw(vol_l));
+ es9218_write(ES9218_REG_VOLUME_RIGHT, dig_vol_to_hw(vol_r));
+}
+
+void es9218_set_amp_volume(int vol)
+{
+ es9218_update(ES9218_REG_ANALOG_VOL, 0x1f, amp_vol_to_hw(vol));
+}
+
+void es9218_mute(bool en)
+{
+ es9218_update(ES9218_REG_FILTER_SYS_MUTE, 1, en ? 1 : 0);
+}
+
+void es9218_set_filter(enum es9218_filter_type filt)
+{
+ es9218_update(ES9218_REG_FILTER_SYS_MUTE, 0xe0, ((int)filt & 7) << 5);
+}
+
+void es9218_set_automute_time(int time)
+{
+ if(time < 0) time = 0;
+ if(time > 255) time = 255;
+ es9218_write(ES9218_REG_AUTOMUTE_TIME, time);
+}
+
+void es9218_set_automute_level(int dB)
+{
+ es9218_update(ES9218_REG_AUTOMUTE_LEVEL, 0x7f, dB);
+}
+
+void es9218_set_automute_fast_mode(bool en)
+{
+ es9218_update(ES9218_REG_MIX_AUTOMUTE, 0x10, en ? 0x10 : 0x00);
+}
+
+void es9218_set_dpll_bandwidth(int knob)
+{
+ es9218_update(ES9218_REG_ASRC_DPLL_BANDWIDTH, 0xf0, (knob & 0xf) << 4);
+}
+
+void es9218_set_thd_compensation(bool en)
+{
+ es9218_update(ES9218_REG_THD_COMP_BYPASS, 0x40, en ? 0x40 : 0);
+}
+
+void es9218_set_thd_coeffs(uint16_t c2, uint16_t c3)
+{
+ es9218_write(ES9218_REG_THD_COMP_C2_LO, c2 & 0xff);
+ es9218_write(ES9218_REG_THD_COMP_C2_HI, (c2 >> 8) & 0xff);
+ es9218_write(ES9218_REG_THD_COMP_C3_LO, c3 & 0xff);
+ es9218_write(ES9218_REG_THD_COMP_C3_HI, (c3 >> 8) & 0xff);
+}
+
+int es9218_read(int reg)
+{
+ return i2c_reg_read1(ES9218_BUS, ES9218_ADDR, reg);
+}
+
+void es9218_write(int reg, uint8_t val)
+{
+ i2c_reg_write1(ES9218_BUS, ES9218_ADDR, reg, val);
+}
+
+void es9218_update(int reg, uint8_t msk, uint8_t val)
+{
+ i2c_reg_modify1(ES9218_BUS, ES9218_ADDR, reg, msk, val, NULL);
+}
diff --git a/firmware/drivers/audio/fiiolinux_codec.c b/firmware/drivers/audio/fiiolinux_codec.c
index 31731fd6ed..99cff3f5e4 100644
--- a/firmware/drivers/audio/fiiolinux_codec.c
+++ b/firmware/drivers/audio/fiiolinux_codec.c
@@ -32,7 +32,6 @@
#include "pcm-alsa.h"
#include <sys/ioctl.h>
-static int fd_hw = -1;
static int ak_hw = -1;
static int vol_sw[2] = {0};
@@ -40,10 +39,6 @@ static long int vol_hw[2] = {0};
static void hw_open(void)
{
- fd_hw = open("/dev/snd/controlC0", O_RDWR);
- if(fd_hw < 0)
- panicf("Cannot open '/dev/snd/controlC0'");
-
ak_hw = open("/dev/ak4376", O_RDWR);
if(ak_hw < 0)
panicf("Cannot open '/dev/ak4376'");
@@ -61,8 +56,7 @@ static void hw_close(void)
panicf("Call cmd AK4376_POWER_OFF fail");
}
close(ak_hw);
- close(fd_hw);
- ak_hw = fd_hw = -1;
+ ak_hw = -1;
}
void audiohw_preinit(void)
@@ -98,7 +92,7 @@ void audiohw_set_volume(int vol_l, int vol_r)
{
int vol[2];
- if (fd_hw < 0)
+ if (ak_hw < 0)
return;
vol[0] = vol_l / 20;
@@ -138,7 +132,7 @@ void audiohw_mute(int mute)
{
long int vol0 = 0;
- if (fd_hw < 0 || muted == mute)
+ if (ak_hw < 0 || muted == mute)
return;
muted = mute;
@@ -159,14 +153,14 @@ void audiohw_mute(int mute)
void audiohw_set_filter_roll_off(int value)
{
- if (fd_hw < 0)
+#if 0 // defined(FIIO_M3K_LINUX)
+ if (ak_hw < 0)
return;
/* 0 = Sharp;
1 = Slow;
2 = Short Sharp
3 = Short Slow */
-#if 0 // defined(FIIO_M3K_LINUX)
// AK4376 supports this but the control isn't wired into ALSA!
long int value_hw = value;
alsa_controls_set_ints("AK4376 Digital Filter", 1, &value_hw);
diff --git a/firmware/drivers/audio/rocker_codec.c b/firmware/drivers/audio/rocker_codec.c
index 87257c92a4..20e45d5afb 100644
--- a/firmware/drivers/audio/rocker_codec.c
+++ b/firmware/drivers/audio/rocker_codec.c
@@ -27,30 +27,16 @@
#include "panic.h"
#include "alsa-controls.h"
-static int fd_hw = -1;
+static int hw_init = 0;
static long int vol_l_hw = 255;
static long int vol_r_hw = 255;
static int muted = -1;
-static void hw_open(void)
-{
- fd_hw = open("/dev/snd/controlC0", O_RDWR);
- if(fd_hw < 0)
- panicf("Cannot open '/dev/snd/controlC0'");
-}
-
-static void hw_close(void)
-{
- close(fd_hw);
- fd_hw = -1;
- muted = -1;
-}
-
void audiohw_mute(int mute)
{
- if (fd_hw < 0 || muted == mute)
+ if (!hw_init || muted == mute)
return;
muted = mute;
@@ -70,7 +56,7 @@ void audiohw_mute(int mute)
void audiohw_preinit(void)
{
alsa_controls_init("default");
- hw_open();
+ hw_init = 1;
#if defined(AUDIOHW_MUTE_ON_STOP) || defined(AUDIOHW_NEEDS_INITIAL_UNMUTE)
audiohw_mute(true); /* Start muted to avoid the POP */
#else
@@ -84,7 +70,8 @@ void audiohw_postinit(void)
void audiohw_close(void)
{
- hw_close();
+ hw_init = 0;
+ muted = -1;
alsa_controls_close();
}
@@ -98,7 +85,7 @@ void audiohw_set_volume(int vol_l, int vol_r)
vol_l_hw = -vol_l/5;
vol_r_hw = -vol_r/5;
- if (fd_hw < 0)
+ if (!hw_init)
return;
alsa_controls_set_ints("Left Playback Volume", 1, &vol_l_hw);
diff --git a/firmware/drivers/audio/x1000-codec.c b/firmware/drivers/audio/x1000-codec.c
new file mode 100644
index 0000000000..c083882dab
--- /dev/null
+++ b/firmware/drivers/audio/x1000-codec.c
@@ -0,0 +1,286 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021-2022 Aidan MacDonald
+ * Copyright 2014 Ingenic Semiconductor Co.,Ltd
+ * cscheng <shicheng.cheng@ingenic.com>
+ * sound/soc/ingenic/icodec/icdc_d3.c
+ * ALSA SoC Audio driver -- ingenic internal codec (icdc_d3) driver
+ *
+ * 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 "x1000-codec.h"
+#include "audiohw.h"
+#include "pcm_sampr.h"
+#include "kernel.h"
+#include "x1000/aic.h"
+
+static const uint8_t fsel_to_hw[HW_NUM_FREQ] = {
+ [0 ... HW_NUM_FREQ-1] = 0,
+ HW_HAVE_8_([HW_FREQ_8] = 0,)
+ HW_HAVE_11_([HW_FREQ_11] = 1,)
+ HW_HAVE_12_([HW_FREQ_12] = 2,)
+ HW_HAVE_16_([HW_FREQ_16] = 3,)
+ HW_HAVE_22_([HW_FREQ_22] = 4,)
+ HW_HAVE_24_([HW_FREQ_24] = 5,)
+ HW_HAVE_32_([HW_FREQ_32] = 6,)
+ HW_HAVE_44_([HW_FREQ_44] = 7,)
+ HW_HAVE_48_([HW_FREQ_48] = 8,)
+ HW_HAVE_88_([HW_FREQ_88] = 9,)
+ HW_HAVE_96_([HW_FREQ_96] = 10,)
+ HW_HAVE_176_([HW_FREQ_176] = 11,)
+ HW_HAVE_192_([HW_FREQ_192] = 12,)
+};
+
+void x1000_icodec_open(void)
+{
+ /* Ingenic does not specify any timing constraints for reset,
+ * let's do a 1ms delay for fun */
+ jz_writef(AIC_RGADW, ICRST(1));
+ mdelay(1);
+ jz_writef(AIC_RGADW, ICRST(0));
+
+ /* Power-up and initial config sequence */
+ static const uint8_t init_config[] = {
+ JZCODEC_CR_VIC, 0x03, /* ensure codec is powered off */
+ JZCODEC_CR_CK, 0x40, /* MCLK_DIV=1, SHUTDOWN_CLK=0, CRYSTAL=12Mhz */
+ JZCODEC_AICR_DAC, 0x13, /* ADWL=0 (16bit word length)
+ * SLAVE=0 (i2s master mode)
+ * SB_DAC=1 (power down DAC)
+ * AUDIOIF=3 (i2s mode) */
+ JZCODEC_AICR_ADC, 0x13, /* ADWL=0 (16bit word length)
+ * SB_ADC=1 (power down ADC)
+ * AUDIOIF=3 (i2s mode)
+ */
+ JZCODEC_CR_DAC, 0x91, /* DAC mute, power down */
+ JZCODEC_CR_DAC2, 0x38, /* DAC power down */
+ JZCODEC_CR_DMIC, 0x00, /* DMIC clock off */
+ JZCODEC_CR_MIC1, 0x30, /* MIC1 power down */
+ JZCODEC_CR_MIC2, 0x30, /* MIC2 power down */
+ JZCODEC_CR_ADC, 0x90, /* ADC mute, power down */
+ JZCODEC_ICR, 0x00, /* INT_FORM=0 (high level IRQ) */
+ JZCODEC_IMR, 0xff, /* Mask all interrupts */
+ JZCODEC_IMR2, 0xff,
+ JZCODEC_IFR, 0xff, /* Clear all interrupt flags */
+ JZCODEC_IFR2, 0xff,
+ };
+
+ for(size_t i = 0; i < ARRAYLEN(init_config); i += 2)
+ x1000_icodec_write(init_config[i], init_config[i+1]);
+
+ /* SB -> 0 (power up) */
+ x1000_icodec_write(JZCODEC_CR_VIC, 0x02);
+ mdelay(250);
+
+ /* Initial gain setting. Apparently we need to set one gain and
+ * then set another after 10ms; afterward it can be changed freely. */
+ static const uint8_t gain_regs[] = {
+ JZCODEC_GCR_DACL,
+ JZCODEC_GCR_DACR,
+ JZCODEC_GCR_DACL2,
+ JZCODEC_GCR_DACR2,
+ JZCODEC_GCR_MIC1,
+ JZCODEC_GCR_MIC2,
+ JZCODEC_GCR_ADCL,
+ JZCODEC_GCR_ADCR,
+ };
+
+ for(size_t i = 0; i < ARRAYLEN(gain_regs); ++i)
+ x1000_icodec_write(gain_regs[i], 0);
+
+ mdelay(10);
+
+ for(size_t i = 0; i < ARRAYLEN(gain_regs); ++i)
+ x1000_icodec_write(gain_regs[i], 1);
+
+ /* SB_SLEEP -> 0 (exit sleep/standby mode) */
+ x1000_icodec_write(JZCODEC_CR_VIC, 0x00);
+ mdelay(200);
+}
+
+void x1000_icodec_close(void)
+{
+ /* SB_SLEEP -> 1 (enable sleep mode) */
+ x1000_icodec_write(JZCODEC_CR_VIC, 0x02);
+
+ /* SB -> 1 (power down) */
+ x1000_icodec_write(JZCODEC_CR_VIC, 0x03);
+}
+
+/*
+ * DAC configuration
+ */
+
+void x1000_icodec_dac_frequency(int fsel)
+{
+ x1000_icodec_update(JZCODEC_FCR_DAC, 0x0f, fsel_to_hw[fsel]);
+}
+
+/*
+ * ADC configuration
+ */
+
+void x1000_icodec_adc_enable(bool en)
+{
+ x1000_icodec_update(JZCODEC_AICR_ADC, 0x10, en ? 0x00 : 0x10);
+ x1000_icodec_update(JZCODEC_CR_ADC, 0x10, en ? 0x00 : 0x10);
+}
+
+void x1000_icodec_adc_mute(bool muted)
+{
+ x1000_icodec_update(JZCODEC_CR_ADC, 0x80, muted ? 0x80 : 0x00);
+}
+
+void x1000_icodec_adc_mic_sel(int sel)
+{
+ x1000_icodec_update(JZCODEC_CR_ADC, 0x40,
+ sel == JZCODEC_MIC_SEL_DIGITAL ? 0x40 : 0x00);
+}
+
+void x1000_icodec_adc_frequency(int fsel)
+{
+ x1000_icodec_update(JZCODEC_FCR_ADC, 0x0f, fsel_to_hw[fsel]);
+}
+
+void x1000_icodec_adc_highpass_filter(bool en)
+{
+ x1000_icodec_update(JZCODEC_FCR_ADC, 0x40, en ? 0x40 : 0x00);
+}
+
+void x1000_icodec_adc_gain(int gain_dB)
+{
+ if(gain_dB < X1000_ICODEC_ADC_GAIN_MIN)
+ gain_dB = X1000_ICODEC_ADC_GAIN_MIN;
+ else if(gain_dB > X1000_ICODEC_ADC_GAIN_MAX)
+ gain_dB = X1000_ICODEC_ADC_GAIN_MAX;
+
+ /* bit 7 = use the same gain for both channels */
+ x1000_icodec_write(JZCODEC_GCR_ADCL, 0x80 | gain_dB);
+}
+
+/*
+ * MIC1 configuration
+ */
+
+void x1000_icodec_mic1_enable(bool en)
+{
+ x1000_icodec_update(JZCODEC_CR_MIC1, 0x10, en ? 0x00 : 0x10);
+}
+
+void x1000_icodec_mic1_bias_enable(bool en)
+{
+ x1000_icodec_update(JZCODEC_CR_MIC1, 0x20, en ? 0x00 : 0x20);
+}
+
+void x1000_icodec_mic1_configure(int settings)
+{
+ x1000_icodec_update(JZCODEC_CR_MIC1, JZCODEC_MIC1_CONFIGURE_MASK,
+ settings & JZCODEC_MIC1_CONFIGURE_MASK);
+}
+
+void x1000_icodec_mic1_gain(int gain_dB)
+{
+ if(gain_dB < X1000_ICODEC_MIC_GAIN_MIN)
+ gain_dB = X1000_ICODEC_MIC_GAIN_MIN;
+ else if(gain_dB > X1000_ICODEC_MIC_GAIN_MAX)
+ gain_dB = X1000_ICODEC_MIC_GAIN_MAX;
+
+ x1000_icodec_write(JZCODEC_GCR_MIC1, gain_dB/X1000_ICODEC_MIC_GAIN_STEP);
+}
+
+/*
+ * Mixer configuration
+ */
+
+void x1000_icodec_mixer_enable(bool en)
+{
+ x1000_icodec_update(JZCODEC_CR_MIX, 0x80, en ? 0x80 : 0x00);
+}
+
+/*
+ * Register access
+ */
+
+static int x1000_icodec_read_direct(int reg)
+{
+ jz_writef(AIC_RGADW, ADDR(reg));
+ return jz_readf(AIC_RGDATA, DATA);
+}
+
+static void x1000_icodec_write_direct(int reg, int value)
+{
+ jz_writef(AIC_RGADW, ADDR(reg), DATA(value));
+ jz_writef(AIC_RGADW, RGWR(1));
+ while(jz_readf(AIC_RGADW, RGWR));
+}
+
+static void x1000_icodec_update_direct(int reg, int mask, int value)
+{
+ int x = x1000_icodec_read_direct(reg) & ~mask;
+ x |= value;
+ x1000_icodec_write_direct(reg, x);
+}
+
+static int x1000_icodec_read_indirect(int c_reg, int index)
+{
+ x1000_icodec_update_direct(c_reg, 0x7f, index & 0x3f);
+ return x1000_icodec_read_direct(c_reg+1);
+}
+
+static void x1000_icodec_write_indirect(int c_reg, int index, int value)
+{
+ /* NB: The X1000 programming manual says we should write the data
+ * register first, but in fact the control register needs to be
+ * written first (following Ingenic's Linux driver). */
+ x1000_icodec_update_direct(c_reg, 0x7f, 0x40 | (index & 0x3f));
+ x1000_icodec_write_direct(c_reg+1, value);
+}
+
+static void x1000_icodec_update_indirect(int c_reg, int index, int mask, int value)
+{
+ int x = x1000_icodec_read_indirect(c_reg, index) & ~mask;
+ x |= value;
+ x1000_icodec_write_indirect(c_reg, index, x);
+}
+
+int x1000_icodec_read(int reg)
+{
+ if(reg & JZCODEC_INDIRECT_BIT)
+ return x1000_icodec_read_indirect(JZCODEC_INDIRECT_CREG(reg),
+ JZCODEC_INDIRECT_INDEX(reg));
+ else
+ return x1000_icodec_read_direct(reg);
+}
+
+void x1000_icodec_write(int reg, int value)
+{
+ if(reg & JZCODEC_INDIRECT_BIT)
+ return x1000_icodec_write_indirect(JZCODEC_INDIRECT_CREG(reg),
+ JZCODEC_INDIRECT_INDEX(reg), value);
+ else
+ return x1000_icodec_write_direct(reg, value);
+}
+
+void x1000_icodec_update(int reg, int mask, int value)
+{
+ if(reg & JZCODEC_INDIRECT_BIT)
+ return x1000_icodec_update_indirect(JZCODEC_INDIRECT_CREG(reg),
+ JZCODEC_INDIRECT_INDEX(reg),
+ mask, value);
+ else
+ return x1000_icodec_update_direct(reg, mask, value);
+}
diff --git a/firmware/drivers/audio/xduoolinux_codec.c b/firmware/drivers/audio/xduoolinux_codec.c
index c78864db2c..3a25f6f7cf 100644
--- a/firmware/drivers/audio/xduoolinux_codec.c
+++ b/firmware/drivers/audio/xduoolinux_codec.c
@@ -78,32 +78,19 @@ X3ii:
*/
-static int fd_hw = -1;
+static int hw_init = 0;
static long int vol_l_hw = 255;
static long int vol_r_hw = 255;
static long int last_ps = -1;
-static void hw_open(void)
-{
- fd_hw = open("/dev/snd/controlC0", O_RDWR);
- if(fd_hw < 0)
- panicf("Cannot open '/dev/snd/controlC0'");
-}
-
-static void hw_close(void)
-{
- close(fd_hw);
- fd_hw = -1;
-}
-
static int muted = -1;
void audiohw_mute(int mute)
{
logf("mute %d", mute);
- if (fd_hw < 0 || muted == mute)
+ if (!hw_init || muted == mute)
return;
muted = mute;
@@ -125,7 +112,7 @@ int xduoo_get_outputs(void){
int status = 0;
- if (fd_hw < 0) return ps;
+ if (!hw_init) return ps;
const char * const sysfs_lo_switch = "/sys/class/switch/lineout/state";
const char * const sysfs_hs_switch = "/sys/class/switch/headset/state";
@@ -151,7 +138,7 @@ int xduoo_get_outputs(void){
void xduoo_set_output(int ps)
{
- if (fd_hw < 0 || muted) return;
+ if (!hw_init || muted) return;
if (last_ps != ps)
{
@@ -172,7 +159,7 @@ void audiohw_preinit(void)
{
logf("hw preinit");
alsa_controls_init("default");
- hw_open();
+ hw_init = 1;
#if defined(XDUOO_X3II)
audiohw_mute(true); /* Start muted to avoid the POP */
@@ -191,7 +178,7 @@ void audiohw_postinit(void)
void audiohw_close(void)
{
logf("hw close");
- hw_close();
+ hw_init = 0;
alsa_controls_close();
}
@@ -217,7 +204,7 @@ void audiohw_set_volume(int vol_l, int vol_r)
r = -vol_r/5;
}
- if (fd_hw < 0)
+ if (!hw_init)
return;
alsa_controls_set_ints("Left Playback Volume", 1, &l);
@@ -251,7 +238,8 @@ void audiohw_set_filter_roll_off(int value)
/* 0 = Sharp;
1 = Slow;
2 = Short Sharp
- 3 = Short Slow */
+ 3 = Short Slow
+ 4 = Super Slow */
#if defined(XDUOO_X3II)
long int value_hw = value;
alsa_controls_set_ints("AK4490 Digital Filter", 1, &value_hw);
diff --git a/firmware/drivers/axp-pmu.c b/firmware/drivers/axp-pmu.c
new file mode 100644
index 0000000000..d59fbb2e3f
--- /dev/null
+++ b/firmware/drivers/axp-pmu.c
@@ -0,0 +1,550 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 "axp-pmu.h"
+#include "power.h"
+#include "system.h"
+#include "i2c-async.h"
+#include <string.h>
+
+/* Headers for the debug menu */
+#ifndef BOOTLOADER
+# include "action.h"
+# include "list.h"
+# include <stdio.h>
+#endif
+
+struct axp_adc_info {
+ uint8_t reg;
+ uint8_t en_reg;
+ uint8_t en_bit;
+ int8_t num;
+ int8_t den;
+};
+
+struct axp_supply_info {
+ uint8_t volt_reg;
+ uint8_t volt_reg_mask;
+ uint8_t en_reg;
+ uint8_t en_bit;
+ int min_mV;
+ int max_mV;
+ int step_mV;
+};
+
+static const struct axp_adc_info axp_adc_info[NUM_ADC_CHANNELS] = {
+ [ADC_ACIN_VOLTAGE] = {0x56, AXP_REG_ADCENABLE1, 1 << 5, 17, 10},
+ [ADC_ACIN_CURRENT] = {0x58, AXP_REG_ADCENABLE1, 1 << 4, 5, 8},
+ [ADC_VBUS_VOLTAGE] = {0x5a, AXP_REG_ADCENABLE1, 1 << 3, 17, 10},
+ [ADC_VBUS_CURRENT] = {0x5c, AXP_REG_ADCENABLE1, 1 << 2, 3, 8},
+ [ADC_INTERNAL_TEMP] = {0x5e, AXP_REG_ADCENABLE2, 1 << 7, 0, 0},
+ [ADC_TS_INPUT] = {0x62, AXP_REG_ADCENABLE1, 1 << 1, 4, 5},
+ [ADC_BATTERY_VOLTAGE] = {0x78, AXP_REG_ADCENABLE1, 1 << 7, 11, 10},
+ [ADC_CHARGE_CURRENT] = {0x7a, AXP_REG_ADCENABLE1, 1 << 6, 1, 2},
+ [ADC_DISCHARGE_CURRENT] = {0x7c, AXP_REG_ADCENABLE1, 1 << 6, 1, 2},
+ [ADC_APS_VOLTAGE] = {0x7e, AXP_REG_ADCENABLE1, 1 << 1, 7, 5},
+};
+
+static const struct axp_supply_info axp_supply_info[AXP_NUM_SUPPLIES] = {
+#if HAVE_AXP_PMU == 192
+ [AXP_SUPPLY_DCDC1] = {
+ .volt_reg = 0x26,
+ .volt_reg_mask = 0x7f,
+ .en_reg = 0x12,
+ .en_bit = 0,
+ .min_mV = 700,
+ .max_mV = 3500,
+ .step_mV = 25,
+ },
+ [AXP_SUPPLY_DCDC2] = {
+ .volt_reg = 0x23,
+ .volt_reg_mask = 0x3f,
+ .en_reg = 0x10,
+ .en_bit = 0,
+ .min_mV = 700,
+ .max_mV = 2275,
+ .step_mV = 25,
+ },
+ [AXP_SUPPLY_DCDC3] = {
+ .volt_reg = 0x27,
+ .volt_reg_mask = 0x7f,
+ .en_reg = 0x12,
+ .en_bit = 1,
+ .min_mV = 700,
+ .max_mV = 3500,
+ .step_mV = 25,
+ },
+ /*
+ * NOTE: LDO1 is always on, and we can't query it or change voltages
+ */
+ [AXP_SUPPLY_LDO2] = {
+ .volt_reg = 0x28,
+ .volt_reg_mask = 0xf0,
+ .en_reg = 0x12,
+ .en_bit = 2,
+ .min_mV = 1800,
+ .max_mV = 3300,
+ .step_mV = 100,
+ },
+ [AXP_SUPPLY_LDO3] = {
+ .volt_reg = 0x28,
+ .volt_reg_mask = 0x0f,
+ .en_reg = 0x12,
+ .en_bit = 3,
+ .min_mV = 1800,
+ .max_mV = 3300,
+ .step_mV = 100,
+ },
+ [AXP_SUPPLY_LDO_IO0] = {
+ .volt_reg = 0x91,
+ .volt_reg_mask = 0xf0,
+ .en_reg = 0x90,
+ .en_bit = 0xff, /* this one requires special handling */
+ .min_mV = 1800,
+ .max_mV = 3300,
+ .step_mV = 100,
+ },
+#else
+# error "Untested AXP chip"
+#endif
+};
+
+void axp_init(void)
+{
+}
+
+void axp_supply_set_voltage(int supply, int voltage)
+{
+ const struct axp_supply_info* info = &axp_supply_info[supply];
+ if(info->volt_reg == 0 || info->volt_reg_mask == 0)
+ return;
+
+ if(voltage > 0 && info->step_mV != 0) {
+ if(voltage < info->min_mV || voltage > info->max_mV)
+ return;
+
+ int regval = (voltage - info->min_mV) / info->step_mV;
+ i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR, info->volt_reg,
+ info->volt_reg_mask, regval, NULL);
+ }
+
+ if(info->en_bit != 0xff) {
+ i2c_reg_setbit1(AXP_PMU_BUS, AXP_PMU_ADDR,
+ info->en_reg, info->en_bit,
+ voltage > 0 ? 1 : 0, NULL);
+ }
+}
+
+int axp_supply_get_voltage(int supply)
+{
+ const struct axp_supply_info* info = &axp_supply_info[supply];
+ if(info->volt_reg == 0)
+ return AXP_SUPPLY_NOT_PRESENT;
+
+ if(info->en_reg != 0) {
+ int r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, info->en_reg);
+ if(r < 0)
+ return AXP_SUPPLY_DISABLED;
+
+#if HAVE_AXP_PMU == 192
+ if(supply == AXP_SUPPLY_LDO_IO0) {
+ if((r & 7) != 2)
+ return AXP_SUPPLY_DISABLED;
+ } else
+#endif
+ {
+ if(r & (1 << info->en_bit) == 0)
+ return AXP_SUPPLY_DISABLED;
+ }
+ }
+
+ /* Hack, avoid undefined shift below. Can be useful too... */
+ if(info->volt_reg_mask == 0)
+ return info->min_mV;
+
+ int r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, info->volt_reg);
+ if(r < 0)
+ return 0;
+
+ int bit = find_first_set_bit(info->volt_reg_mask);
+ int val = (r & info->volt_reg_mask) >> bit;
+ return info->min_mV + (val * info->step_mV);
+}
+
+/* TODO: this can STILL indicate some false positives! */
+int axp_battery_status(void)
+{
+ int r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP_REG_POWERSTATUS);
+ if(r >= 0) {
+ /* Charging bit indicates we're currently charging */
+ if((r & 0x04) != 0)
+ return AXP_BATT_CHARGING;
+
+ /* Not plugged in means we're discharging */
+ if((r & 0xf0) == 0)
+ return AXP_BATT_DISCHARGING;
+ } else {
+ /* Report discharging if we can't find out power status */
+ return AXP_BATT_DISCHARGING;
+ }
+
+ /* If the battery is full and not in use, the charging bit will be 0,
+ * there will be an external power source, AND the discharge current
+ * will be zero. Seems to rule out all false positives. */
+ int d = axp_adc_read_raw(ADC_DISCHARGE_CURRENT);
+ if(d == 0)
+ return AXP_BATT_FULL;
+
+ return AXP_BATT_DISCHARGING;
+}
+
+int axp_input_status(void)
+{
+#ifdef HAVE_BATTERY_SWITCH
+ int input_status = 0;
+#else
+ int input_status = AXP_INPUT_BATTERY;
+#endif
+
+ int r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP_REG_POWERSTATUS);
+ if(r < 0)
+ return input_status;
+
+ /* Check for AC input */
+ if(r & 0x80)
+ input_status |= AXP_INPUT_AC;
+
+ /* Only report USB if ACIN and VBUS are not shorted */
+ if((r & 0x20) != 0 && (r & 0x02) == 0)
+ input_status |= AXP_INPUT_USB;
+
+#ifdef HAVE_BATTERY_SWITCH
+ /* Check for battery presence if target defines it as removable */
+ r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP_REG_CHARGESTATUS);
+ if(r >= 0 && (r & 0x20) != 0)
+ input_status |= AXP_INPUT_BATTERY;
+#endif
+
+ return input_status;
+}
+
+int axp_adc_read(int adc)
+{
+ int value = axp_adc_read_raw(adc);
+ if(value == INT_MIN)
+ return INT_MIN;
+
+ return axp_adc_conv_raw(adc, value);
+}
+
+int axp_adc_read_raw(int adc)
+{
+ /* Read the ADC */
+ uint8_t buf[2];
+ uint8_t reg = axp_adc_info[adc].reg;
+ int rc = i2c_reg_read(AXP_PMU_BUS, AXP_PMU_ADDR, reg, 2, &buf[0]);
+ if(rc != I2C_STATUS_OK)
+ return INT_MIN;
+
+ /* Parse the value */
+ if(adc == ADC_CHARGE_CURRENT || adc == ADC_DISCHARGE_CURRENT)
+ return (buf[0] << 5) | (buf[1] & 0x1f);
+ else
+ return (buf[0] << 4) | (buf[1] & 0xf);
+}
+
+int axp_adc_conv_raw(int adc, int value)
+{
+ if(adc == ADC_INTERNAL_TEMP)
+ return value - 1447;
+ else
+ return axp_adc_info[adc].num * value / axp_adc_info[adc].den;
+}
+
+void axp_adc_set_enabled(int adc_bits)
+{
+ uint8_t xfer[3];
+ xfer[0] = 0;
+ xfer[1] = AXP_REG_ADCENABLE2;
+ xfer[2] = 0;
+
+ /* Compute the new register values */
+ const struct axp_adc_info* info = axp_adc_info;
+ for(int i = 0; i < NUM_ADC_CHANNELS; ++i) {
+ if(!(adc_bits & (1 << i)))
+ continue;
+
+ if(info[i].en_reg == AXP_REG_ADCENABLE1)
+ xfer[0] |= info[i].en_bit;
+ else
+ xfer[2] |= info[i].en_bit;
+ }
+
+ /* Update the configuration */
+ i2c_reg_write(AXP_PMU_BUS, AXP_PMU_ADDR, AXP_REG_ADCENABLE1, 3, &xfer[0]);
+}
+
+int axp_adc_get_rate(void)
+{
+ int r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP_REG_ADCSAMPLERATE);
+ if(r < 0)
+ return AXP_ADC_RATE_100HZ; /* an arbitrary value */
+
+ return (r >> 6) & 3;
+}
+
+void axp_adc_set_rate(int rate)
+{
+ i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP_REG_ADCSAMPLERATE,
+ 0xc0, (rate & 3) << 6, NULL);
+}
+
+static uint32_t axp_cc_parse(const uint8_t* buf)
+{
+ return ((uint32_t)buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+}
+
+void axp_cc_read(uint32_t* charge, uint32_t* discharge)
+{
+ uint8_t buf[8];
+ int rc = i2c_reg_read(AXP_PMU_BUS, AXP_PMU_ADDR,
+ AXP_REG_COULOMBCOUNTERBASE, 8, &buf[0]);
+ if(rc != I2C_STATUS_OK) {
+ if(charge)
+ *charge = 0;
+ if(discharge)
+ *discharge = 0;
+ return;
+ }
+
+ if(charge)
+ *charge = axp_cc_parse(&buf[0]);
+ if(discharge)
+ *discharge = axp_cc_parse(&buf[4]);
+}
+
+void axp_cc_clear(void)
+{
+ i2c_reg_setbit1(AXP_PMU_BUS, AXP_PMU_ADDR,
+ AXP_REG_COULOMBCOUNTERCTRL, 5, 1, NULL);
+}
+
+void axp_cc_enable(bool en)
+{
+ i2c_reg_setbit1(AXP_PMU_BUS, AXP_PMU_ADDR,
+ AXP_REG_COULOMBCOUNTERCTRL, 7, en ? 1 : 0, NULL);
+}
+
+bool axp_cc_is_enabled(void)
+{
+ int reg = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR,
+ AXP_REG_COULOMBCOUNTERCTRL);
+ return reg >= 0 && (reg & 0x40) != 0;
+}
+
+static const int chargecurrent_tbl[] = {
+ 100, 190, 280, 360,
+ 450, 550, 630, 700,
+ 780, 880, 960, 1000,
+ 1080, 1160, 1240, 1320,
+};
+
+void axp_set_charge_current(int current_mA)
+{
+ /* find greatest charging current not exceeding requested current */
+ unsigned int index = 0;
+ while(index < ARRAYLEN(chargecurrent_tbl)-1 &&
+ chargecurrent_tbl[index+1] <= current_mA)
+ ++index;
+
+ i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
+ AXP_REG_CHARGECONTROL1, 0x0f, index, NULL);
+}
+
+int axp_get_charge_current(void)
+{
+ int ret = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR,
+ AXP_REG_CHARGECONTROL1);
+ if(ret < 0)
+ ret = 0;
+
+ return chargecurrent_tbl[ret & 0x0f];
+}
+
+void axp_power_off(void)
+{
+ /* Set the shutdown bit */
+ i2c_reg_setbit1(AXP_PMU_BUS, AXP_PMU_ADDR,
+ AXP_REG_SHUTDOWNLEDCTRL, 7, 1, NULL);
+}
+
+#ifndef BOOTLOADER
+enum {
+ AXP_DEBUG_BATTERY_STATUS,
+ AXP_DEBUG_INPUT_STATUS,
+ AXP_DEBUG_CHARGE_CURRENT,
+ AXP_DEBUG_COULOMB_COUNTERS,
+ AXP_DEBUG_ADC_RATE,
+ AXP_DEBUG_FIRST_ADC,
+ AXP_DEBUG_FIRST_SUPPLY = AXP_DEBUG_FIRST_ADC + NUM_ADC_CHANNELS,
+ AXP_DEBUG_NUM_ENTRIES = AXP_DEBUG_FIRST_SUPPLY + AXP_NUM_SUPPLIES,
+};
+
+static int axp_debug_menu_cb(int action, struct gui_synclist* lists)
+{
+ (void)lists;
+
+ if(action == ACTION_NONE)
+ action = ACTION_REDRAW;
+
+ return action;
+}
+
+static const char* axp_debug_menu_get_name(int item, void* data,
+ char* buf, size_t buflen)
+{
+ (void)data;
+
+ static const char* const adc_names[] = {
+ "V_acin", "I_acin", "V_vbus", "I_vbus", "T_int",
+ "V_ts", "V_batt", "I_chrg", "I_dchg", "V_aps", "P_batt"
+ };
+
+ static const char* const adc_units[] = {
+ "mV", "mA", "mV", "mA", "C", "mV", "mV", "mA", "mA", "mV", "uW",
+ };
+
+ static const char* const supply_names[] = {
+ "DCDC1", "DCDC2", "DCDC3",
+ "LDO1", "LDO2", "LDO3", "LDO_IO0",
+ };
+
+ int adc = item - AXP_DEBUG_FIRST_ADC;
+ if(item >= AXP_DEBUG_FIRST_ADC && adc < NUM_ADC_CHANNELS) {
+ int raw_value = axp_adc_read_raw(adc);
+ if(raw_value == INT_MIN) {
+ snprintf(buf, buflen, "%s: [Disabled]", adc_names[adc]);
+ return buf;
+ }
+
+ int value = axp_adc_conv_raw(adc, raw_value);
+ if(adc == ADC_INTERNAL_TEMP) {
+ snprintf(buf, buflen, "%s: %d.%d %s", adc_names[adc],
+ value/10, value%10, adc_units[adc]);
+ } else {
+ snprintf(buf, buflen, "%s: %d %s", adc_names[adc],
+ value, adc_units[adc]);
+ }
+
+ return buf;
+ }
+
+ int supply = item - AXP_DEBUG_FIRST_SUPPLY;
+ if(item >= AXP_DEBUG_FIRST_SUPPLY && supply < AXP_NUM_SUPPLIES) {
+ int voltage = axp_supply_get_voltage(supply);
+ if(voltage == AXP_SUPPLY_NOT_PRESENT)
+ snprintf(buf, buflen, "%s: [Not Present]", supply_names[supply]);
+ else if(voltage == AXP_SUPPLY_DISABLED)
+ snprintf(buf, buflen, "%s: [Disabled]", supply_names[supply]);
+ else
+ snprintf(buf, buflen, "%s: %d mV", supply_names[supply], voltage);
+
+ return buf;
+ }
+
+ switch(item) {
+ case AXP_DEBUG_BATTERY_STATUS: {
+ switch(axp_battery_status()) {
+ case AXP_BATT_FULL:
+ return "Battery: Full";
+ case AXP_BATT_CHARGING:
+ return "Battery: Charging";
+ case AXP_BATT_DISCHARGING:
+ return "Battery: Discharging";
+ default:
+ return "Battery: Unknown";
+ }
+ } break;
+
+ case AXP_DEBUG_INPUT_STATUS: {
+ int s = axp_input_status();
+ const char* ac = (s & AXP_INPUT_AC) ? " AC" : "";
+ const char* usb = (s & AXP_INPUT_USB) ? " USB" : "";
+ const char* batt = (s & AXP_INPUT_BATTERY) ? " Battery" : "";
+ snprintf(buf, buflen, "Inputs:%s%s%s", ac, usb, batt);
+ return buf;
+ } break;
+
+ case AXP_DEBUG_CHARGE_CURRENT: {
+ int current = axp_get_charge_current();
+ snprintf(buf, buflen, "Max charge current: %d mA", current);
+ return buf;
+ } break;
+
+ case AXP_DEBUG_COULOMB_COUNTERS: {
+ uint32_t charge, discharge;
+ axp_cc_read(&charge, &discharge);
+
+ snprintf(buf, buflen, "Coulomb counters: +%lu / -%lu",
+ (unsigned long)charge, (unsigned long)discharge);
+ return buf;
+ } break;
+
+ case AXP_DEBUG_ADC_RATE: {
+ int rate = 25 << axp_adc_get_rate();
+ snprintf(buf, buflen, "ADC sample rate: %d Hz", rate);
+ return buf;
+ } break;
+
+ default:
+ return "---";
+ }
+}
+
+bool axp_debug_menu(void)
+{
+ struct simplelist_info info;
+ simplelist_info_init(&info, "AXP debug", AXP_DEBUG_NUM_ENTRIES, NULL);
+ info.action_callback = axp_debug_menu_cb;
+ info.get_name = axp_debug_menu_get_name;
+ return simplelist_show_list(&info);
+}
+#endif /* !BOOTLOADER */
+
+/* This is basically the only valid implementation, so define it here */
+unsigned int power_input_status(void)
+{
+ unsigned int state = 0;
+ int input_status = axp_input_status();
+
+ if(input_status & AXP_INPUT_AC)
+ state |= POWER_INPUT_MAIN_CHARGER;
+
+ if(input_status & AXP_INPUT_USB)
+ state |= POWER_INPUT_USB_CHARGER;
+
+#ifdef HAVE_BATTERY_SWITCH
+ if(input_status & AXP_INPUT_BATTERY)
+ state |= POWER_INPUT_BATTERY;
+#endif
+
+ return state;
+}
diff --git a/firmware/drivers/axp173.c b/firmware/drivers/axp173.c
deleted file mode 100644
index 22417650fc..0000000000
--- a/firmware/drivers/axp173.c
+++ /dev/null
@@ -1,419 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2021 Aidan MacDonald
- *
- * 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 "axp173.h"
-#include "power.h"
-#include "i2c-async.h"
-
-/* Headers for the debug menu */
-#ifndef BOOTLOADER
-# include "action.h"
-# include "list.h"
-# include <stdio.h>
-#endif
-
-static const struct axp173_adc_info {
- uint8_t reg;
- uint8_t en_reg;
- uint8_t en_bit;
-} axp173_adc_info[NUM_ADC_CHANNELS] = {
- {0x56, 0x82, 5}, /* ACIN_VOLTAGE */
- {0x58, 0x82, 4}, /* ACIN_CURRENT */
- {0x5a, 0x82, 3}, /* VBUS_VOLTAGE */
- {0x5c, 0x82, 2}, /* VBUS_CURRENT */
- {0x5e, 0x83, 7}, /* INTERNAL_TEMP */
- {0x62, 0x82, 1}, /* TS_INPUT */
- {0x78, 0x82, 7}, /* BATTERY_VOLTAGE */
- {0x7a, 0x82, 6}, /* CHARGE_CURRENT */
- {0x7c, 0x82, 6}, /* DISCHARGE_CURRENT */
- {0x7e, 0x82, 1}, /* APS_VOLTAGE */
- {0x70, 0xff, 0}, /* BATTERY_POWER */
-};
-
-static struct axp173 {
- int adc_enable;
-} axp173;
-
-static void axp173_init_enabled_adcs(void)
-{
- axp173.adc_enable = 0;
-
- /* Read enabled ADCs from the hardware */
- uint8_t regs[2];
- int rc = i2c_reg_read(AXP173_BUS, AXP173_ADDR, 0x82, 2, &regs[0]);
- if(rc != I2C_STATUS_OK)
- return;
-
- /* Parse registers to set ADC enable bits */
- const struct axp173_adc_info* info = axp173_adc_info;
- for(int i = 0; i < NUM_ADC_CHANNELS; ++i) {
- if(info[i].en_reg == 0xff)
- continue;
-
- if(regs[info[i].en_reg - 0x82] & info[i].en_bit)
- axp173.adc_enable |= 1 << i;
- }
-
- /* Handle battery power ADC */
- if((axp173.adc_enable & (1 << ADC_BATTERY_VOLTAGE)) &&
- (axp173.adc_enable & (1 << ADC_DISCHARGE_CURRENT))) {
- axp173.adc_enable |= (1 << ADC_BATTERY_POWER);
- }
-}
-
-void axp173_init(void)
-{
- axp173_init_enabled_adcs();
-
- /* We need discharge current ADC to reliably poll for a full battery */
- int bits = axp173.adc_enable;
- bits |= (1 << ADC_DISCHARGE_CURRENT);
- axp173_adc_set_enabled(bits);
-}
-
-/* TODO: this can STILL indicate some false positives! */
-int axp173_battery_status(void)
-{
- int r = i2c_reg_read1(AXP173_BUS, AXP173_ADDR, 0x00);
- if(r >= 0) {
- /* Charging bit indicates we're currently charging */
- if((r & 0x04) != 0)
- return AXP173_BATT_CHARGING;
-
- /* Not plugged in means we're discharging */
- if((r & 0xf0) == 0)
- return AXP173_BATT_DISCHARGING;
- } else {
- /* Report discharging if we can't find out power status */
- return AXP173_BATT_DISCHARGING;
- }
-
- /* If the battery is full and not in use, the charging bit will be 0,
- * there will be an external power source, AND the discharge current
- * will be zero. Seems to rule out all false positives. */
- int d = axp173_adc_read_raw(ADC_DISCHARGE_CURRENT);
- if(d == 0)
- return AXP173_BATT_FULL;
-
- return AXP173_BATT_DISCHARGING;
-}
-
-int axp173_input_status(void)
-{
-#ifdef HAVE_BATTERY_SWITCH
- int input_status = 0;
-#else
- int input_status = AXP173_INPUT_BATTERY;
-#endif
-
- int r = i2c_reg_read1(AXP173_BUS, AXP173_ADDR, 0x00);
- if(r < 0)
- return input_status;
-
- /* Check for AC input */
- if(r & 0x80)
- input_status |= AXP173_INPUT_AC;
-
- /* Only report USB if ACIN and VBUS are not shorted */
- if((r & 0x20) != 0 && (r & 0x02) == 0)
- input_status |= AXP173_INPUT_USB;
-
-#ifdef HAVE_BATTERY_SWITCH
- /* Check for battery presence if target defines it as removable */
- r = i2c_reg_read1(AXP173_BUS, AXP173_ADDR, 0x01);
- if(r >= 0 && (r & 0x20) != 0)
- input_status |= AXP173_INPUT_BATTERY;
-#endif
-
- return input_status;
-}
-
-int axp173_adc_read(int adc)
-{
- int value = axp173_adc_read_raw(adc);
- if(value == INT_MIN)
- return INT_MIN;
-
- return axp173_adc_conv_raw(adc, value);
-}
-
-int axp173_adc_read_raw(int adc)
-{
- /* Don't give a reading if the ADC is not enabled */
- if((axp173.adc_enable & (1 << adc)) == 0)
- return INT_MIN;
-
- /* Read the ADC */
- uint8_t buf[3];
- int count = (adc == ADC_BATTERY_POWER) ? 3 : 2;
- uint8_t reg = axp173_adc_info[adc].reg;
- int rc = i2c_reg_read(AXP173_BUS, AXP173_ADDR, reg, count, &buf[0]);
- if(rc != I2C_STATUS_OK)
- return INT_MIN;
-
- /* Parse the value */
- if(adc == ADC_BATTERY_POWER)
- return (buf[0] << 16) | (buf[1] << 8) | buf[2];
- else if(adc == ADC_CHARGE_CURRENT || adc == ADC_DISCHARGE_CURRENT)
- return (buf[0] << 5) | (buf[1] & 0x1f);
- else
- return (buf[0] << 4) | (buf[1] & 0xf);
-}
-
-int axp173_adc_conv_raw(int adc, int value)
-{
- switch(adc) {
- case ADC_ACIN_VOLTAGE:
- case ADC_VBUS_VOLTAGE:
- /* 0 mV ... 6.9615 mV, step 1.7 mV */
- return value * 17 / 10;
- case ADC_ACIN_CURRENT:
- /* 0 mA ... 2.5594 A, step 0.625 mA */
- return value * 5 / 8;
- case ADC_VBUS_CURRENT:
- /* 0 mA ... 1.5356 A, step 0.375 mA */
- return value * 3 / 8;
- case ADC_INTERNAL_TEMP:
- /* -144.7 C ... 264.8 C, step 0.1 C */
- return value - 1447;
- case ADC_TS_INPUT:
- /* 0 mV ... 3.276 V, step 0.8 mV */
- return value * 4 / 5;
- case ADC_BATTERY_VOLTAGE:
- /* 0 mV ... 4.5045 V, step 1.1 mV */
- return value * 11 / 10;
- case ADC_CHARGE_CURRENT:
- case ADC_DISCHARGE_CURRENT:
- /* 0 mA to 4.095 A, step 0.5 mA */
- return value / 2;
- case ADC_APS_VOLTAGE:
- /* 0 mV to 5.733 V, step 1.4 mV */
- return value * 7 / 5;
- case ADC_BATTERY_POWER:
- /* 0 uW to 23.6404 W, step 0.55 uW */
- return value * 11 / 20;
- default:
- /* Shouldn't happen */
- return INT_MIN;
- }
-}
-
-int axp173_adc_get_enabled(void)
-{
- return axp173.adc_enable;
-}
-
-void axp173_adc_set_enabled(int adc_bits)
-{
- /* Ignore no-op */
- if(adc_bits == axp173.adc_enable)
- return;
-
- /* Compute the new register values */
- const struct axp173_adc_info* info = axp173_adc_info;
- uint8_t regs[2] = {0, 0};
- for(int i = 0; i < NUM_ADC_CHANNELS; ++i) {
- if(info[i].en_reg == 0xff)
- continue;
-
- if(adc_bits & (1 << i))
- regs[info[i].en_reg - 0x82] |= 1 << info[i].en_bit;
- }
-
- /* These ADCs share an enable bit */
- if(adc_bits & ((1 << ADC_CHARGE_CURRENT)|(1 << ADC_DISCHARGE_CURRENT))) {
- adc_bits |= (1 << ADC_CHARGE_CURRENT);
- adc_bits |= (1 << ADC_DISCHARGE_CURRENT);
- }
-
- /* Enable required bits for battery power ADC */
- if(adc_bits & (1 << ADC_BATTERY_POWER)) {
- regs[0] |= 1 << info[ADC_DISCHARGE_CURRENT].en_bit;
- regs[0] |= 1 << info[ADC_BATTERY_VOLTAGE].en_bit;
- }
-
- /* Update the configuration */
- i2c_reg_write(AXP173_BUS, AXP173_ADDR, 0x82, 2, &regs[0]);
- axp173.adc_enable = adc_bits;
-}
-
-int axp173_adc_get_rate(void)
-{
- int r = i2c_reg_read1(AXP173_BUS, AXP173_ADDR, 0x84);
- if(r < 0)
- return AXP173_ADC_RATE_100HZ; /* an arbitrary value */
-
- return (r >> 6) & 3;
-}
-
-void axp173_adc_set_rate(int rate)
-{
- i2c_reg_modify1(AXP173_BUS, AXP173_ADDR, 0x84,
- 0xc0, (rate & 3) << 6, NULL);
-}
-
-static uint32_t axp173_cc_parse(const uint8_t* buf)
-{
- return ((uint32_t)buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
-}
-
-void axp173_cc_read(uint32_t* charge, uint32_t* discharge)
-{
- uint8_t buf[8];
- int rc = i2c_reg_read(AXP173_BUS, AXP173_ADDR, 0xb0, 8, &buf[0]);
- if(rc != I2C_STATUS_OK) {
- if(charge)
- *charge = 0;
- if(discharge)
- *discharge = 0;
- return;
- }
-
- if(charge)
- *charge = axp173_cc_parse(&buf[0]);
- if(discharge)
- *discharge = axp173_cc_parse(&buf[4]);
-}
-
-void axp173_cc_clear(void)
-{
- i2c_reg_setbit1(AXP173_BUS, AXP173_ADDR, 0xb8, 5, 1, NULL);
-}
-
-void axp173_cc_enable(bool en)
-{
- i2c_reg_setbit1(AXP173_BUS, AXP173_ADDR, 0xb8, 7, en ? 1 : 0, NULL);
-}
-
-#ifndef BOOTLOADER
-#define AXP173_DEBUG_BATTERY_STATUS 0
-#define AXP173_DEBUG_INPUT_STATUS 1
-#define AXP173_DEBUG_ADC_RATE 2
-#define AXP173_DEBUG_FIRST_ADC 3
-#define AXP173_DEBUG_ENTRIES (AXP173_DEBUG_FIRST_ADC + NUM_ADC_CHANNELS)
-
-static int axp173_debug_menu_cb(int action, struct gui_synclist* lists)
-{
- (void)lists;
-
- if(action == ACTION_NONE)
- action = ACTION_REDRAW;
-
- return action;
-}
-
-static const char* axp173_debug_menu_get_name(int item, void* data,
- char* buf, size_t buflen)
-{
- (void)data;
-
- static const char* const adc_names[] = {
- "V_acin", "I_acin", "V_vbus", "I_vbus", "T_int",
- "V_ts", "V_batt", "I_chrg", "I_dchg", "V_aps", "P_batt"
- };
-
- static const char* const adc_units[] = {
- "mV", "mA", "mV", "mA", "C", "mV", "mV", "mA", "mA", "mV", "uW",
- };
-
- int adc = item - AXP173_DEBUG_FIRST_ADC;
- if(item >= AXP173_DEBUG_FIRST_ADC && adc < NUM_ADC_CHANNELS) {
- int raw_value = axp173_adc_read_raw(adc);
- if(raw_value == INT_MIN) {
- snprintf(buf, buflen, "%s: [Disabled]", adc_names[adc]);
- return buf;
- }
-
- int value = axp173_adc_conv_raw(adc, raw_value);
- if(adc == ADC_INTERNAL_TEMP) {
- snprintf(buf, buflen, "%s: %d.%d %s", adc_names[adc],
- value/10, value%10, adc_units[adc]);
- } else {
- snprintf(buf, buflen, "%s: %d %s", adc_names[adc],
- value, adc_units[adc]);
- }
-
- return buf;
- }
-
- switch(item) {
- case AXP173_DEBUG_BATTERY_STATUS: {
- switch(axp173_battery_status()) {
- case AXP173_BATT_FULL:
- return "Battery: Full";
- case AXP173_BATT_CHARGING:
- return "Battery: Charging";
- case AXP173_BATT_DISCHARGING:
- return "Battery: Discharging";
- default:
- return "Battery: Unknown";
- }
- } break;
-
- case AXP173_DEBUG_INPUT_STATUS: {
- int s = axp173_input_status();
- const char* ac = (s & AXP173_INPUT_AC) ? " AC" : "";
- const char* usb = (s & AXP173_INPUT_USB) ? " USB" : "";
- const char* batt = (s & AXP173_INPUT_BATTERY) ? " Battery" : "";
- snprintf(buf, buflen, "Inputs:%s%s%s", ac, usb, batt);
- return buf;
- } break;
-
- case AXP173_DEBUG_ADC_RATE: {
- int rate = 25 << axp173_adc_get_rate();
- snprintf(buf, buflen, "ADC sample rate: %d Hz", rate);
- return buf;
- } break;
-
- default:
- return "---";
- }
-}
-
-bool axp173_debug_menu(void)
-{
- struct simplelist_info info;
- simplelist_info_init(&info, "AXP173 debug", AXP173_DEBUG_ENTRIES, NULL);
- info.action_callback = axp173_debug_menu_cb;
- info.get_name = axp173_debug_menu_get_name;
- return simplelist_show_list(&info);
-}
-#endif /* !BOOTLOADER */
-
-/* This is basically the only valid implementation, so define it here */
-unsigned int power_input_status(void)
-{
- unsigned int state = 0;
- int input_status = axp173_input_status();
-
- if(input_status & AXP173_INPUT_AC)
- state |= POWER_INPUT_MAIN_CHARGER;
-
- if(input_status & AXP173_INPUT_USB)
- state |= POWER_INPUT_USB_CHARGER;
-
-#ifdef HAVE_BATTERY_SWITCH
- if(input_status & AXP173_INPUT_BATTERY)
- state |= POWER_INPUT_BATTERY;
-#endif
-
- return state;
-}
diff --git a/firmware/drivers/button.c b/firmware/drivers/button.c
index 5addfee3de..3220f18ed7 100644
--- a/firmware/drivers/button.c
+++ b/firmware/drivers/button.c
@@ -51,18 +51,14 @@ static long lastbtn; /* Last valid button status */
static long last_read; /* Last button status, for debouncing/filtering */
static intptr_t button_data; /* data value from last message dequeued */
static bool flipped; /* buttons can be flipped to match the LCD flip */
-#ifdef HAVE_BACKLIGHT
-static bool filter_first_keypress;
+
+#ifdef HAVE_BACKLIGHT /* Filter first keypress function pointer */
+static bool (*keypress_filter_fn)(int, int);
#ifdef HAVE_REMOTE_LCD
-static bool remote_filter_first_keypress;
+static bool (*remote_keypress_filter_fn)(int, int);
#endif
#endif /* HAVE_BACKLIGHT */
-#ifdef HAVE_HEADPHONE_DETECTION
-static bool phones_present = false;
-#endif
-#ifdef HAVE_LINEOUT_DETECTION
-static bool lineout_present = false;
-#endif
+
#ifdef HAVE_SW_POWEROFF
static bool enable_sw_poweroff = true;
#endif
@@ -70,41 +66,40 @@ static bool enable_sw_poweroff = true;
/* how long until repeat kicks in, in centiseconds */
#define REPEAT_START (30*HZ/100)
-#ifndef HAVE_TOUCHSCREEN
-/* the next two make repeat "accelerate", which is nice for lists
+/* The next two make repeat "accelerate", which is nice for lists
* which begin to scroll a bit faster when holding until the
- * real list accerelation kicks in (this smoothes acceleration)
+ * real list acceleration kicks in (this smooths acceleration).
+ *
+ * Note that touchscreen pointing events are not subject to this
+ * acceleration and always use REPEAT_INTERVAL_TOUCH. (Do repeat
+ * events even do anything sane for touchscreens??)
*/
/* the speed repeat starts at, in centiseconds */
#define REPEAT_INTERVAL_START (16*HZ/100)
/* speed repeat finishes at, in centiseconds */
#define REPEAT_INTERVAL_FINISH (5*HZ/100)
-#else
-/*
- * on touchscreen it's different, scrolling is done by swiping over the
- * screen (potentially very quickly) and is completely different from button
- * targets
- * So, on touchscreen we don't want to artifically slow down early repeats,
- * it'd have the contrary effect of making rockbox appear lagging
- */
-#define REPEAT_INTERVAL_START (5*HZ/100)
-#define REPEAT_INTERVAL_FINISH (5*HZ/100)
-#endif
+/* repeat interval for touch events */
+#define REPEAT_INTERVAL_TOUCH (5*HZ/100)
-#ifdef HAVE_BUTTON_DATA
-static int button_read(int *data);
static int lastdata = 0;
-#else
-static int button_read(void);
-#endif
+static int button_read(int *data);
#ifdef HAVE_TOUCHSCREEN
-static int last_touchscreen_touch;
+static long last_touchscreen_touch;
#endif
-#if defined(HAVE_HEADPHONE_DETECTION)
-static struct timeout hp_detect_timeout; /* Debouncer for headphone plug/unplug */
+static void button_remote_post(void)
+{
+#if defined(HAS_SERIAL_REMOTE) && !defined(SIMULATOR)
+ /* Post events for the remote control */
+ int btn = remote_control_rx();
+ if(btn)
+ button_try_post(btn, 0);
+#endif
+}
+
+#if defined(HAVE_HEADPHONE_DETECTION)
static int hp_detect_callback(struct timeout *tmo)
{
/* Try to post only transistions */
@@ -117,8 +112,6 @@ static int hp_detect_callback(struct timeout *tmo)
#endif
#if defined(HAVE_LINEOUT_DETECTION)
-static struct timeout lo_detect_timeout; /* Debouncer for lineout plug/unplug */
-
static int lo_detect_callback(struct timeout *tmo)
{
/* Try to post only transistions */
@@ -130,6 +123,34 @@ static int lo_detect_callback(struct timeout *tmo)
}
#endif
+static void check_audio_peripheral_state(void)
+{
+#if defined(HAVE_HEADPHONE_DETECTION)
+ static struct timeout hp_detect_timeout; /* Debouncer for headphone plug/unplug */
+ static bool phones_present = false;
+
+ if (headphones_inserted() != phones_present)
+ {
+ /* Use the autoresetting oneshot to debounce the detection signal */
+ phones_present = !phones_present;
+ timeout_register(&hp_detect_timeout, hp_detect_callback,
+ HZ/2, phones_present);
+ }
+#endif
+#if defined(HAVE_LINEOUT_DETECTION)
+ static struct timeout lo_detect_timeout; /* Debouncer for lineout plug/unplug */
+ static bool lineout_present = false;
+
+ if (lineout_inserted() != lineout_present)
+ {
+ /* Use the autoresetting oneshot to debounce the detection signal */
+ lineout_present = !lineout_present;
+ timeout_register(&lo_detect_timeout, lo_detect_callback,
+ HZ/2, lineout_present);
+ }
+#endif
+}
+
static bool button_try_post(int button, int data)
{
#ifdef HAVE_TOUCHSCREEN
@@ -159,6 +180,43 @@ static bool button_try_post(int button, int data)
return ret;
}
+#ifdef HAVE_BACKLIGHT
+/* disabled function is shared between Main & Remote LCDs */
+static bool filter_first_keypress_disabled(int button, int data)
+{
+ button_try_post(button, data);
+ return false;
+}
+
+static bool filter_first_keypress_enabled(int button, int data)
+{
+#if defined(HAVE_TRANSFLECTIVE_LCD) && defined(HAVE_LCD_SLEEP)
+ if (is_backlight_on(false) && lcd_active())
+#else
+ if (is_backlight_on(false))
+#endif
+ {
+ return filter_first_keypress_disabled(button, data);
+ }
+ return true;
+}
+
+#ifdef HAVE_REMOTE_LCD
+static bool filter_first_remote_keypress_enabled(int button, int data)
+{
+ if (is_remote_backlight_on(false)
+#if defined(IRIVER_H100_SERIES) || defined(IRIVER_H300_SERIES)
+ || (remote_type()==REMOTETYPE_H300_NONLCD)
+#endif
+ )
+ {
+ return filter_first_keypress_disabled(button, data);
+ }
+ return true;
+}
+#endif /* def HAVE_REMOTE_LCD */
+#endif /* def HAVE_BACKLIGHT */
+
static void button_tick(void)
{
static int count = 0;
@@ -174,42 +232,13 @@ static void button_tick(void)
#endif
int diff;
int btn;
-#ifdef HAVE_BUTTON_DATA
int data = 0;
-#else
- const int data = 0;
-#endif
-#if defined(HAS_SERIAL_REMOTE) && !defined(SIMULATOR)
- /* Post events for the remote control */
- btn = remote_control_rx();
- if(btn)
- button_try_post(btn, 0);
-#endif
+ button_remote_post();
-#ifdef HAVE_BUTTON_DATA
btn = button_read(&data);
-#else
- btn = button_read();
-#endif
-#if defined(HAVE_HEADPHONE_DETECTION)
- if (headphones_inserted() != phones_present)
- {
- /* Use the autoresetting oneshot to debounce the detection signal */
- phones_present = !phones_present;
- timeout_register(&hp_detect_timeout, hp_detect_callback,
- HZ/2, phones_present);
- }
-#endif
-#if defined(HAVE_LINEOUT_DETECTION)
- if (lineout_inserted() != lineout_present)
- {
- /* Use the autoresetting oneshot to debounce the detection signal */
- lineout_present = !lineout_present;
- timeout_register(&lo_detect_timeout, lo_detect_callback,
- HZ/2, lineout_present);
- }
-#endif
+
+ check_audio_peripheral_state();
/* Find out if a key has been released */
diff = btn ^ lastbtn;
@@ -275,6 +304,11 @@ static void button_tick(void)
/* yes we have repeat */
if (repeat_speed > REPEAT_INTERVAL_FINISH)
repeat_speed--;
+#ifdef HAVE_TOUCHSCREEN
+ if(btn & BUTTON_TOUCHSCREEN)
+ repeat_speed = REPEAT_INTERVAL_TOUCH;
+#endif
+
count = repeat_speed;
repeat_count++;
@@ -349,48 +383,21 @@ static void button_tick(void)
#ifdef HAVE_BACKLIGHT
#ifdef HAVE_REMOTE_LCD
if (btn & BUTTON_REMOTE) {
- if (!remote_filter_first_keypress
- || is_remote_backlight_on(false)
-#if defined(IRIVER_H100_SERIES) || defined(IRIVER_H300_SERIES)
- || (remote_type()==REMOTETYPE_H300_NONLCD)
-#endif
- )
- button_try_post(btn, data);
- else
- skip_remote_release = true;
+ skip_remote_release = remote_keypress_filter_fn(btn, data);
+ remote_backlight_on();
}
else
#endif
- if (!filter_first_keypress
-#if defined(HAVE_TRANSFLECTIVE_LCD) && defined(HAVE_LCD_SLEEP)
- || (is_backlight_on(false) && lcd_active())
-#else
- || is_backlight_on(false)
-#endif
-#if BUTTON_REMOTE
- || (btn & BUTTON_REMOTE)
-#endif
- )
- button_try_post(btn, data);
- else
- skip_release = true;
+ {
+ skip_release = keypress_filter_fn(btn, data);
+ backlight_on();
+ buttonlight_on();
+ }
#else /* no backlight, nothing to skip */
button_try_post(btn, data);
#endif
post = false;
}
-#ifdef HAVE_REMOTE_LCD
- if(btn & BUTTON_REMOTE)
- remote_backlight_on();
- else
-#endif
- {
- backlight_on();
-#ifdef HAVE_BUTTON_LIGHT
- buttonlight_on();
-#endif
- }
-
reset_poweroff_timer();
}
}
@@ -401,9 +408,8 @@ static void button_tick(void)
}
}
lastbtn = btn & ~(BUTTON_REL | BUTTON_REPEAT);
-#ifdef HAVE_BUTTON_DATA
+
lastdata = data;
-#endif
}
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
@@ -516,34 +522,27 @@ intptr_t button_get_data(void)
void button_init(void)
{
+ int temp;
/* Init used objects first */
queue_init(&button_queue, true);
-#ifdef HAVE_BUTTON_DATA
- int temp;
-#endif
/* hardware inits */
button_init_device();
-#ifdef HAVE_BUTTON_DATA
button_read(&temp);
lastbtn = button_read(&temp);
-#else
- button_read();
- lastbtn = button_read();
-#endif
reset_poweroff_timer();
flipped = false;
#ifdef HAVE_BACKLIGHT
- filter_first_keypress = false;
+ set_backlight_filter_keypress(false);
#ifdef HAVE_REMOTE_LCD
- remote_filter_first_keypress = false;
+ set_remote_backlight_filter_keypress(false);
#endif
#endif
#ifdef HAVE_TOUCHSCREEN
- last_touchscreen_touch = 0xffff;
+ last_touchscreen_touch = -1;
#endif
/* Start polling last */
tick_add_task(button_tick);
@@ -650,12 +649,18 @@ void button_set_flip(bool flip)
#ifdef HAVE_BACKLIGHT
void set_backlight_filter_keypress(bool value)
{
- filter_first_keypress = value;
+ if (!value)
+ keypress_filter_fn = filter_first_keypress_disabled;
+ else
+ keypress_filter_fn = filter_first_keypress_enabled;
}
#ifdef HAVE_REMOTE_LCD
void set_remote_backlight_filter_keypress(bool value)
{
- remote_filter_first_keypress = value;
+ if (!value)
+ remote_keypress_filter_fn = filter_first_keypress_disabled;
+ else
+ remote_keypress_filter_fn = filter_first_remote_keypress_enabled;
}
#endif
#endif
@@ -663,13 +668,13 @@ void set_remote_backlight_filter_keypress(bool value)
/*
* Get button pressed from hardware
*/
-#ifdef HAVE_BUTTON_DATA
+
static int button_read(int *data)
{
+#ifdef HAVE_BUTTON_DATA
int btn = button_read_device(data);
#else
-static int button_read(void)
-{
+ (void) data;
int btn = button_read_device();
#endif
int retval;
@@ -715,7 +720,7 @@ void button_clear_queue(void)
}
#ifdef HAVE_TOUCHSCREEN
-int touchscreen_last_touch(void)
+long touchscreen_last_touch(void)
{
return last_touchscreen_touch;
}
diff --git a/firmware/drivers/cw2015.c b/firmware/drivers/cw2015.c
new file mode 100644
index 0000000000..705ca16e22
--- /dev/null
+++ b/firmware/drivers/cw2015.c
@@ -0,0 +1,191 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 "cw2015.h"
+#include "i2c-async.h"
+#include <string.h>
+#include "system.h"
+
+/* Headers for the debug menu */
+#ifndef BOOTLOADER
+# include "action.h"
+# include "list.h"
+# include <stdio.h>
+#endif
+
+/* Battery profile info is an opaque blob. According to this,
+ * https://lore.kernel.org/linux-pm/20200503154855.duwj2djgqfiyleq5@earth.universe/T/#u
+ * the blob only comes from Cellwise testing a physical battery and cannot be
+ * obtained any other way. It's specific to a given battery so each target has
+ * its own profile.
+ *
+ * Profile data seems to be retained on the chip so it's not a hard requirement
+ * to define this. Provided you don't lose power in the meantime, it should be
+ * enough to just boot the OF, then boot Rockbox and read out the battery info
+ * from the CW2015 debug screen.
+ */
+#if defined(SHANLING_Q1)
+static const uint8_t device_batinfo[CW2015_SIZE_BATINFO] = {
+ 0x15, 0x7E, 0x61, 0x59, 0x57, 0x55, 0x56, 0x4C,
+ 0x4E, 0x4D, 0x50, 0x4C, 0x45, 0x3A, 0x2D, 0x27,
+ 0x22, 0x1E, 0x19, 0x1E, 0x2A, 0x3C, 0x48, 0x45,
+ 0x1D, 0x94, 0x08, 0xF6, 0x15, 0x29, 0x48, 0x51,
+ 0x5D, 0x60, 0x63, 0x66, 0x45, 0x1D, 0x83, 0x38,
+ 0x09, 0x43, 0x16, 0x42, 0x76, 0x98, 0xA5, 0x1B,
+ 0x41, 0x76, 0x99, 0xBF, 0x80, 0xC0, 0xEF, 0xCB,
+ 0x2F, 0x00, 0x64, 0xA5, 0xB5, 0x0E, 0x30, 0x29,
+};
+#else
+# define NO_BATINFO
+#endif
+
+static uint8_t chip_batinfo[CW2015_SIZE_BATINFO];
+
+/* TODO: Finish implementing this
+ *
+ * Although this chip might give a better battery estimate than voltage does,
+ * the mainline linux driver has a lot of weird hacks due to oddities like the
+ * SoC getting stuck during charging, and from limited testing it seems this
+ * may occur for the Q1 too.
+ */
+
+static int cw2015_read_bat_info(uint8_t* data)
+{
+ for(int i = 0; i < CW2015_SIZE_BATINFO; ++i) {
+ int r = i2c_reg_read1(CW2015_BUS, CW2015_ADDR, CW2015_REG_BATINFO + i);
+ if(r < 0)
+ return r;
+
+ data[i] = r & 0xff;
+ }
+
+ return 0;
+}
+
+void cw2015_init(void)
+{
+ /* mdelay(100); */
+ int rc = cw2015_read_bat_info(&chip_batinfo[0]);
+ if(rc < 0)
+ memset(chip_batinfo, 0, sizeof(chip_batinfo));
+}
+
+int cw2015_get_vcell(void)
+{
+ int vcell_msb = i2c_reg_read1(CW2015_BUS, CW2015_ADDR, CW2015_REG_VCELL);
+ int vcell_lsb = i2c_reg_read1(CW2015_BUS, CW2015_ADDR, CW2015_REG_VCELL+1);
+
+ if(vcell_msb < 0 || vcell_lsb < 0)
+ return -1;
+
+ /* 14 bits, resolution 305 uV */
+ int v_raw = ((vcell_msb & 0x3f) << 8) | vcell_lsb;
+ return v_raw * 61 / 200;
+}
+
+int cw2015_get_soc(void)
+{
+ int soc_msb = i2c_reg_read1(CW2015_BUS, CW2015_ADDR, CW2015_REG_SOC);
+
+ if(soc_msb < 0)
+ return -1;
+
+ /* MSB is the state of charge in percentage.
+ * the LSB contains fractional information not useful to Rockbox. */
+ return soc_msb & 0xff;
+}
+
+int cw2015_get_rrt(void)
+{
+ int rrt_msb = i2c_reg_read1(CW2015_BUS, CW2015_ADDR, CW2015_REG_RRT_ALERT);
+ int rrt_lsb = i2c_reg_read1(CW2015_BUS, CW2015_ADDR, CW2015_REG_RRT_ALERT+1);
+
+ if(rrt_msb < 0 || rrt_lsb < 0)
+ return -1;
+
+ /* 13 bits, resolution 1 minute */
+ return ((rrt_msb & 0x1f) << 8) | rrt_lsb;
+}
+
+const uint8_t* cw2015_get_bat_info(void)
+{
+ return &chip_batinfo[0];
+}
+
+#ifndef BOOTLOADER
+enum {
+ CW2015_DEBUG_VCELL = 0,
+ CW2015_DEBUG_SOC,
+ CW2015_DEBUG_RRT,
+ CW2015_DEBUG_BATINFO,
+ CW2015_DEBUG_BATINFO_LAST = CW2015_DEBUG_BATINFO + 7,
+ CW2015_DEBUG_NUM_ENTRIES,
+};
+
+static int cw2015_debug_menu_cb(int action, struct gui_synclist* lists)
+{
+ (void)lists;
+
+ if(action == ACTION_NONE)
+ action = ACTION_REDRAW;
+
+ return action;
+}
+
+static const char* cw2015_debug_menu_get_name(int item, void* data,
+ char* buf, size_t buflen)
+{
+ (void)data;
+
+ /* hexdump of battery info */
+ if(item >= CW2015_DEBUG_BATINFO && item <= CW2015_DEBUG_BATINFO_LAST) {
+ int i = item - CW2015_DEBUG_BATINFO;
+ const uint8_t* batinfo = cw2015_get_bat_info();
+ snprintf(buf, buflen, "BatInfo%d: %02x %02x %02x %02x %02x %02x %02x %02x", i,
+ batinfo[8*i + 0], batinfo[8*i + 1], batinfo[8*i + 2], batinfo[8*i + 3],
+ batinfo[8*i + 4], batinfo[8*i + 5], batinfo[8*i + 6], batinfo[8*i + 7]);
+ return buf;
+ }
+
+ switch(item) {
+ case CW2015_DEBUG_VCELL:
+ snprintf(buf, buflen, "VCell: %d mV", cw2015_get_vcell());
+ return buf;
+ case CW2015_DEBUG_SOC:
+ snprintf(buf, buflen, "SOC: %d%%", cw2015_get_soc());
+ return buf;
+ case CW2015_DEBUG_RRT:
+ snprintf(buf, buflen, "Runtime: %d min", cw2015_get_rrt());
+ return buf;
+ default:
+ return "---";
+ }
+}
+
+bool cw2015_debug_menu(void)
+{
+ struct simplelist_info info;
+ simplelist_info_init(&info, "CW2015 debug", CW2015_DEBUG_NUM_ENTRIES, NULL);
+ info.action_callback = cw2015_debug_menu_cb;
+ info.get_name = cw2015_debug_menu_get_name;
+ return simplelist_show_list(&info);
+}
+#endif
diff --git a/firmware/drivers/fat.c b/firmware/drivers/fat.c
index 50619983e9..20c8f2b8e0 100644
--- a/firmware/drivers/fat.c
+++ b/firmware/drivers/fat.c
@@ -22,7 +22,7 @@
#include "config.h"
#include "system.h"
#include "sys/types.h"
-#include <string.h>
+#include "string-extra.h"
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
@@ -1698,8 +1698,8 @@ static int add_dir_entry(struct bpb *fat_bpb, struct fat_filestr *parentstr,
int rc;
unsigned char basisname[11], shortname[11];
- int n;
int entries_needed;
+ int n = -1;
unsigned long ucslen = 0;
if (is_dotdir_name(name) && (attr & ATTR_DIRECTORY))
@@ -2276,6 +2276,41 @@ fat_error:
return rc;
}
+int fat_modtime(struct fat_file *parent, struct fat_file *file,
+ time_t modtime)
+{
+ struct bpb * const fat_bpb = FAT_BPB(parent->volume);
+
+ if (!fat_bpb)
+ return -1;
+
+ int rc;
+
+ struct fat_filestr parentstr;
+ fat_filestr_init(&parentstr, parent);
+
+ dc_lock_cache();
+
+ union raw_dirent *ent = cache_direntry(fat_bpb, &parentstr, file->e.entry);
+ if (!ent)
+ FAT_ERROR(-2);
+
+ uint16_t date;
+ uint16_t time;
+ dostime_localtime(modtime, &date, &time);
+
+ ent->wrttime = htole16(time);
+ ent->wrtdate = htole16(date);
+ ent->lstaccdate = htole16(date);
+
+ dc_dirty_buf(ent);
+
+ rc = 0;
+fat_error:
+ dc_unlock_cache();
+ cache_commit(fat_bpb);
+ return rc;
+}
/** File stream functions **/
@@ -2384,44 +2419,43 @@ static long transfer(struct bpb *fat_bpb, unsigned long start, long count,
panicf("Write %ld after data\n",
start + count - fat_bpb->totalsectors);
}
- else
- {
- rc = storage_write_sectors(IF_MD(fat_bpb->drive,)
- start + fat_bpb->startsector, count, buf);
- }
}
- else
- {
- void* xferbuf = buf;
-#ifdef STORAGE_NEEDS_BOUNCE_BUFFER
- int remain = count;
- int xferred = 0;
- int aligned = 1;
- if(STORAGE_OVERLAP((uintptr_t)buf)) {
- xferbuf = FAT_BOUNCE_BUFFER(fat_bpb);
- aligned = 0;
- count = MIN(remain, FAT_BOUNCE_SECTORS);
- }
- while(remain > 0) {
-#endif
- rc = storage_read_sectors(IF_MD(fat_bpb->drive,)
- start + fat_bpb->startsector, count, xferbuf);
#ifdef STORAGE_NEEDS_BOUNCE_BUFFER
+ if(UNLIKELY(STORAGE_OVERLAP((uintptr_t)buf))) {
+ void* xfer_buf = FAT_BOUNCE_BUFFER(fat_bpb);
+ while(count > 0) {
+ int xfer_count = MIN(count, FAT_BOUNCE_SECTORS);
+
+ if(write) {
+ memcpy(xfer_buf, buf, xfer_count * SECTOR_SIZE);
+ rc = storage_write_sectors(IF_MD(fat_bpb->drive,)
+ start + fat_bpb->startsector, xfer_count, xfer_buf);
+ } else {
+ rc = storage_read_sectors(IF_MD(fat_bpb->drive,)
+ start + fat_bpb->startsector, xfer_count, xfer_buf);
+ memcpy(buf, xfer_buf, xfer_count * SECTOR_SIZE);
+ }
+
if(rc < 0)
break;
- if(LIKELY(aligned))
- break;
- memcpy(buf, xferbuf, count * SECTOR_SIZE);
- buf += count * SECTOR_SIZE;
- xferred += count;
- start += count;
- remain -= count;
- count = MIN(remain, FAT_BOUNCE_SECTORS);
+ buf += xfer_count * SECTOR_SIZE;
+ start += xfer_count;
+ count -= xfer_count;
}
+ } else {
#endif
+ if(write) {
+ rc = storage_write_sectors(IF_MD(fat_bpb->drive,)
+ start + fat_bpb->startsector, count, buf);
+ } else {
+ rc = storage_read_sectors(IF_MD(fat_bpb->drive,)
+ start + fat_bpb->startsector, count, buf);
+ }
+#ifdef STORAGE_NEEDS_BOUNCE_BUFFER
}
+#endif
if (rc < 0)
{
@@ -2955,20 +2989,6 @@ void fat_empty_fat_direntry(struct fat_direntry *entry)
entry->firstcluster = 0;
}
-time_t fattime_mktime(uint16_t fatdate, uint16_t fattime)
-{
- /* this knows our mktime() only uses these struct tm fields */
- struct tm tm;
- tm.tm_sec = ((fattime ) & 0x1f) * 2;
- tm.tm_min = ((fattime >> 5) & 0x3f);
- tm.tm_hour = ((fattime >> 11) );
- tm.tm_mday = ((fatdate ) & 0x1f);
- tm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
- tm.tm_year = ((fatdate >> 9) ) + 80;
-
- return mktime(&tm);
-}
-
void fat_init(void)
{
dc_lock_cache();
diff --git a/firmware/drivers/ft6x06.c b/firmware/drivers/ft6x06.c
new file mode 100644
index 0000000000..c605ee0944
--- /dev/null
+++ b/firmware/drivers/ft6x06.c
@@ -0,0 +1,129 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 "ft6x06.h"
+#include "kernel.h"
+#include "i2c-async.h"
+#include <string.h>
+
+#define BYTES_PER_POINT 6
+
+#ifdef FT6x06_SWAP_AXES
+# define POS_X pos_y
+# define POS_Y pos_x
+#else
+# define POS_X pos_x
+# define POS_Y pos_y
+#endif
+
+struct ft6x06_driver {
+ /* i2c bus data */
+ int i2c_cookie;
+ i2c_descriptor i2c_desc;
+
+ /* callback for touch events */
+ ft6x06_event_cb event_cb;
+
+ /* buffer for I2C transfers */
+ uint8_t raw_data[1 + 2 + BYTES_PER_POINT * FT6x06_NUM_POINTS];
+};
+
+static struct ft6x06_driver ft_drv;
+struct ft6x06_state ft6x06_state;
+
+static inline void ft6x06_convert_point(const uint8_t* raw,
+ struct ft6x06_point* pt)
+{
+ pt->event = (raw[0] >> 6) & 0x3;
+ pt->touch_id = (raw[2] >> 4) & 0xf;
+ pt->POS_X = ((raw[0] & 0xf) << 8) | raw[1];
+ pt->POS_Y = ((raw[2] & 0xf) << 8) | raw[3];
+ pt->weight = raw[4];
+ pt->area = (raw[5] >> 4) & 0xf;
+}
+
+static void ft6x06_i2c_callback(int status, i2c_descriptor* desc)
+{
+ (void)desc;
+ if(status != I2C_STATUS_OK)
+ return;
+
+ ft6x06_state.gesture = ft_drv.raw_data[1];
+ ft6x06_state.nr_points = ft_drv.raw_data[2] & 0xf;
+ for(int i = 0; i < FT6x06_NUM_POINTS; ++i) {
+ ft6x06_convert_point(&ft_drv.raw_data[3 + i * BYTES_PER_POINT],
+ &ft6x06_state.points[i]);
+ }
+
+ ft_drv.event_cb(&ft6x06_state);
+}
+
+static void ft6x06_dummy_event_cb(struct ft6x06_state* state)
+{
+ (void)state;
+}
+
+void ft6x06_init(void)
+{
+ /* Initialize stuff */
+ memset(&ft_drv, 0, sizeof(ft_drv));
+ ft_drv.event_cb = ft6x06_dummy_event_cb;
+
+ memset(&ft6x06_state, 0, sizeof(struct ft6x06_state));
+ ft6x06_state.gesture = -1;
+ for(int i = 0; i < FT6x06_NUM_POINTS; ++i)
+ ft6x06_state.points[i].event = FT6x06_EVT_NONE;
+
+ /* Reserve bus management cookie */
+ ft_drv.i2c_cookie = i2c_async_reserve_cookies(FT6x06_BUS, 1);
+
+ /* Prep an I2C descriptor to read touch data */
+ ft_drv.i2c_desc.slave_addr = FT6x06_ADDR;
+ ft_drv.i2c_desc.bus_cond = I2C_START | I2C_STOP;
+ ft_drv.i2c_desc.tran_mode = I2C_READ;
+ ft_drv.i2c_desc.buffer[0] = &ft_drv.raw_data[0];
+ ft_drv.i2c_desc.count[0] = 1;
+ ft_drv.i2c_desc.buffer[1] = &ft_drv.raw_data[1];
+ ft_drv.i2c_desc.count[1] = sizeof(ft_drv.raw_data) - 1;
+ ft_drv.i2c_desc.callback = ft6x06_i2c_callback;
+ ft_drv.i2c_desc.arg = 0;
+ ft_drv.i2c_desc.next = NULL;
+
+ /* Set I2C register address */
+ ft_drv.raw_data[0] = 0x01;
+}
+
+void ft6x06_set_event_cb(ft6x06_event_cb cb)
+{
+ ft_drv.event_cb = cb ? cb : ft6x06_dummy_event_cb;
+}
+
+void ft6x06_enable(bool en)
+{
+ i2c_reg_write1(FT6x06_BUS, FT6x06_ADDR, 0xa5, en ? 0 : 3);
+}
+
+void ft6x06_irq_handler(void)
+{
+ /* We don't care if this fails, there's not much we can do about it */
+ i2c_async_queue(FT6x06_BUS, TIMEOUT_NOBLOCK, I2C_Q_ONCE,
+ ft_drv.i2c_cookie, &ft_drv.i2c_desc);
+}
diff --git a/firmware/drivers/generic_i2c.c b/firmware/drivers/generic_i2c.c
index effb5372b4..9fd90b5b2c 100644
--- a/firmware/drivers/generic_i2c.c
+++ b/firmware/drivers/generic_i2c.c
@@ -198,6 +198,40 @@ end:
return ret;
}
+int i2c_write_read_data(int bus_index, int bus_address,
+ const unsigned char* buf_write, int count_write,
+ unsigned char* buf_read, int count_read)
+{
+ int i;
+ int ret = 0;
+ const struct i2c_interface *iface = i2c_if[bus_index];
+
+ i2c_start(iface);
+ if (!i2c_outb(iface, bus_address))
+ {
+ ret = -2;
+ goto end;
+ }
+
+ for(i = 0;i < count_write;i++)
+ {
+ if (!i2c_outb(iface, buf_write[i]))
+ {
+ ret = -3;
+ goto end;
+ }
+ }
+
+ for(i = 0;i < count_read-1;i++)
+ buf_read[i] = i2c_inb(iface, true);
+
+ buf_read[i] = i2c_inb(iface, false);
+
+end:
+ i2c_stop(iface);
+ return ret;
+}
+
/* returns bus index which can be used as a handle, or <0 on error */
int i2c_add_node(const struct i2c_interface *iface)
{
diff --git a/firmware/drivers/isp1583.c b/firmware/drivers/isp1583.c
index c028d2fa01..e60339d9fc 100644
--- a/firmware/drivers/isp1583.c
+++ b/firmware/drivers/isp1583.c
@@ -341,7 +341,7 @@ static void usb_handle_setup_rx(void)
if (len == 8)
{
ISP1583_DFLOW_CTRLFUN |= DFLOW_CTRLFUN_STATUS; /* Acknowledge packet */
- usb_core_control_request((struct usb_ctrlrequest*)setup_pkt_buf);
+ usb_core_legacy_control_request((struct usb_ctrlrequest*)setup_pkt_buf);
}
else
{
@@ -531,9 +531,9 @@ static void in_callback(int ep, unsigned char *buf, int len)
usb_core_transfer_complete(ep, false, 0, len);
}
-int usb_drv_recv(int ep, void* ptr, int length)
+int usb_drv_recv_nonblocking(int ep, void* ptr, int length)
{
- logf("usb_drv_recv(%d, 0x%x, %d)", ep, (int)ptr, length);
+ logf("usb_drv_recv_nonblocking(%d, 0x%x, %d)", ep, (int)ptr, length);
if(ep == EP_CONTROL && length == 0 && ptr == NULL)
{
usb_status_ack(ep, DIR_TX);
diff --git a/firmware/drivers/lcd-16bit-common.c b/firmware/drivers/lcd-16bit-common.c
index 49e515f59f..9d24dfe16e 100644
--- a/firmware/drivers/lcd-16bit-common.c
+++ b/firmware/drivers/lcd-16bit-common.c
@@ -31,38 +31,15 @@
/* Clear the current viewport */
void lcd_clear_viewport(void)
{
+ struct viewport *vp = lcd_current_viewport;
fb_data *dst, *dst_end;
int x, y, width, height;
int len, step;
- x = lcd_current_viewport->x;
- y = lcd_current_viewport->y;
- width = lcd_current_viewport->width;
- height = lcd_current_viewport->height;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
- return;
-
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
+ x = vp->x;
+ y = vp->y;
+ width = vp->width;
+ height = vp->height;
len = STRIDE_MAIN(width, height);
step = STRIDE_MAIN(ROW_INC, COL_INC);
@@ -70,18 +47,18 @@ void lcd_clear_viewport(void)
dst = FBADDR(x, y);
dst_end = FBADDR(x + width - 1 , y + height - 1);
- if (lcd_current_viewport->drawmode & DRMODE_INVERSEVID)
+ if (vp->drawmode & DRMODE_INVERSEVID)
{
do
{
- memset16(dst, lcd_current_viewport->fg_pattern, len);
+ memset16(dst, vp->fg_pattern, len);
dst += step;
}
while (dst <= dst_end);
}
else
{
- if (lcd_backdrop && lcd_current_viewport->buffer == &lcd_framebuffer_default)
+ if (lcd_backdrop && vp->buffer == &lcd_framebuffer_default)
{
do
{
@@ -95,19 +72,19 @@ void lcd_clear_viewport(void)
{
do
{
- memset16(dst, lcd_current_viewport->bg_pattern, len);
+ memset16(dst, vp->bg_pattern, len);
dst += step;
}
while (dst <= dst_end);
}
}
- if (lcd_current_viewport == &default_vp)
+ if (vp == &default_vp)
lcd_scroll_stop();
else
- lcd_scroll_stop_viewport(lcd_current_viewport);
+ lcd_scroll_stop_viewport(vp);
- lcd_current_viewport->flags &= ~(VP_FLAG_VP_SET_CLEAN);
+ vp->flags &= ~(VP_FLAG_VP_SET_CLEAN);
}
/*** low-level drawing functions ***/
@@ -152,69 +129,24 @@ lcd_fastpixelfunc_type* const * lcd_fastpixelfuncs = lcd_fastpixelfuncs_bgcolor;
/* Fill a rectangular area */
void lcd_fillrect(int x, int y, int width, int height)
{
+ struct viewport *vp = lcd_current_viewport;
unsigned bits = 0;
enum fill_opt fillopt = OPT_NONE;
fb_data *dst, *dst_end;
int len, step;
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= lcd_current_viewport->width) ||
- (y >= lcd_current_viewport->height) || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, NULL, NULL))
return;
- if (x < 0)
- {
- width += x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- y = 0;
- }
- if (x + width > lcd_current_viewport->width)
- width = lcd_current_viewport->width - x;
- if (y + height > lcd_current_viewport->height)
- height = lcd_current_viewport->height - y;
-
- /* adjust for viewport */
- x += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
- return;
-
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
-
/* drawmode and optimisation */
- if (lcd_current_viewport->drawmode & DRMODE_INVERSEVID)
+ if (vp->drawmode & DRMODE_INVERSEVID)
{
- if (lcd_current_viewport->drawmode & DRMODE_BG)
+ if (vp->drawmode & DRMODE_BG)
{
if (!lcd_backdrop)
{
fillopt = OPT_SET;
- bits = lcd_current_viewport->bg_pattern;
+ bits = vp->bg_pattern;
}
else
fillopt = OPT_COPY;
@@ -222,13 +154,13 @@ void lcd_fillrect(int x, int y, int width, int height)
}
else
{
- if (lcd_current_viewport->drawmode & DRMODE_FG)
+ if (vp->drawmode & DRMODE_FG)
{
fillopt = OPT_SET;
- bits = lcd_current_viewport->fg_pattern;
+ bits = vp->fg_pattern;
}
}
- if (fillopt == OPT_NONE && lcd_current_viewport->drawmode != DRMODE_COMPLEMENT)
+ if (fillopt == OPT_NONE && vp->drawmode != DRMODE_COMPLEMENT)
return;
dst = FBADDR(x, y);
@@ -283,74 +215,20 @@ void ICODE_ATTR lcd_mono_bitmap_part(const unsigned char *src, int src_x,
int src_y, int stride, int x, int y,
int width, int height)
{
- const unsigned char *src_end;
- fb_data *dst, *dst_col;
- unsigned dmask = 0x100; /* bit 8 == sentinel */
- int drmode = lcd_current_viewport->drawmode;
- int row;
-
- /******************** Image in viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= lcd_current_viewport->width) ||
- (y >= lcd_current_viewport->height) || (x + width <= 0) || (y + height <= 0))
+ struct viewport *vp = lcd_current_viewport;
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, &src_x, &src_y))
return;
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > lcd_current_viewport->width)
- width = lcd_current_viewport->width - x;
- if (y + height > lcd_current_viewport->height)
- height = lcd_current_viewport->height - y;
-
- /* adjust for viewport */
- x += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
- return;
-
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
-
- src += stride * (src_y >> 3) + src_x; /* move starting point */
- src_y &= 7;
- src_end = src + width;
- dst_col = FBADDR(x, y);
+ /* move starting point */
+ src += stride * (src_y >> 3) + src_x;
+ src_y &= 7;
+ unsigned dmask = 0;
+ int drmode = vp->drawmode;
if (drmode & DRMODE_INVERSEVID)
{
- dmask = 0x1ff; /* bit 8 == sentinel */
+ dmask = 0xff;
drmode &= DRMODE_SOLID; /* mask out inversevid */
}
@@ -358,107 +236,99 @@ void ICODE_ATTR lcd_mono_bitmap_part(const unsigned char *src, int src_x,
if ((drmode & DRMODE_BG) && lcd_backdrop)
drmode |= DRMODE_INT_BD;
- /* go through each column and update each pixel */
- do
+ fb_data* dst = FBADDR(x, y);
+ while(height > 0)
{
- const unsigned char *src_col = src++;
- unsigned data = (*src_col ^ dmask) >> src_y;
+ const unsigned char* src_col = src;
+ const unsigned char* src_end = src + width;
+ fb_data* dst_col = dst;
+
+ unsigned data;
int fg, bg;
uintptr_t bo;
- dst = dst_col;
- dst_col += COL_INC;
- row = height;
+ switch (drmode) {
+ case DRMODE_COMPLEMENT:
+ do {
+ data = (*src_col++ ^ dmask) >> src_y;
+ if(data & 0x01)
+ *dst_col = ~(*dst_col);
-#define UPDATE_SRC do { \
- data >>= 1; \
- if (data == 0x001) { \
- src_col += stride; \
- data = *src_col ^ dmask; \
- } \
- } while (0)
-
- switch (drmode)
- {
- case DRMODE_COMPLEMENT:
- do
- {
- if (data & 0x01)
- *dst = ~(*dst);
-
- dst += ROW_INC;
- UPDATE_SRC;
- }
- while (--row);
+ dst_col += COL_INC;
+ } while(src_col != src_end);
break;
- case DRMODE_BG|DRMODE_INT_BD:
+ case DRMODE_BG|DRMODE_INT_BD:
bo = lcd_backdrop_offset;
- do
- {
- if (!(data & 0x01))
- *dst = *PTR_ADD(dst, bo);
+ do {
+ data = (*src_col++ ^ dmask) >> src_y;
+ if(!(data & 0x01))
+ *dst_col = *PTR_ADD(dst_col, bo);
- dst += ROW_INC;
- UPDATE_SRC;
- }
- while (--row);
+ dst_col += COL_INC;
+ } while(src_col != src_end);
break;
case DRMODE_BG:
- bg = lcd_current_viewport->bg_pattern;
- do
- {
- if (!(data & 0x01))
- *dst = bg;
-
- dst += ROW_INC;
- UPDATE_SRC;
- }
- while (--row);
+ bg = vp->bg_pattern;
+ do {
+ data = (*src_col++ ^ dmask) >> src_y;
+ if(!(data & 0x01))
+ *dst_col = bg;
+
+ dst_col += COL_INC;
+ } while(src_col != src_end);
break;
- case DRMODE_FG:
- fg = lcd_current_viewport->fg_pattern;
- do
- {
- if (data & 0x01)
- *dst = fg;
+ case DRMODE_FG:
+ fg = vp->fg_pattern;
+ do {
+ data = (*src_col++ ^ dmask) >> src_y;
+ if(data & 0x01)
+ *dst_col = fg;
- dst += ROW_INC;
- UPDATE_SRC;
- }
- while (--row);
+ dst_col += COL_INC;
+ } while(src_col != src_end);
break;
- case DRMODE_SOLID|DRMODE_INT_BD:
- fg = lcd_current_viewport->fg_pattern;
+ case DRMODE_SOLID|DRMODE_INT_BD:
+ fg = vp->fg_pattern;
bo = lcd_backdrop_offset;
- do
- {
- *dst = (data & 0x01) ? fg
- : *PTR_ADD(dst, bo);
- dst += ROW_INC;
- UPDATE_SRC;
- }
- while (--row);
+ do {
+ data = (*src_col++ ^ dmask) >> src_y;
+ if(data & 0x01)
+ *dst_col = fg;
+ else
+ *dst_col = *PTR_ADD(dst_col, bo);
+
+ dst_col += COL_INC;
+ } while(src_col != src_end);
break;
- case DRMODE_SOLID:
- fg = lcd_current_viewport->fg_pattern;
- bg = lcd_current_viewport->bg_pattern;
- do
- {
- *dst = (data & 0x01) ? fg : bg;
- dst += ROW_INC;
- UPDATE_SRC;
- }
- while (--row);
+ case DRMODE_SOLID:
+ fg = vp->fg_pattern;
+ bg = vp->bg_pattern;
+ do {
+ data = (*src_col++ ^ dmask) >> src_y;
+ if(data & 0x01)
+ *dst_col = fg;
+ else
+ *dst_col = bg;
+
+ dst_col += COL_INC;
+ } while(src_col != src_end);
break;
}
+
+ src_y = (src_y + 1) & 7;
+ if(src_y == 0)
+ src += stride;
+
+ dst += ROW_INC;
+ height--;
}
- while (src < src_end);
}
+
/* Draw a full monochrome bitmap */
void lcd_mono_bitmap(const unsigned char *src, int x, int y, int width, int height)
{
@@ -466,7 +336,7 @@ void lcd_mono_bitmap(const unsigned char *src, int x, int y, int width, int heig
}
-/* About Rockbox' internal alpha channel format (for ALPHA_COLOR_FONT_DEPTH == 2)
+/* About Rockbox' internal alpha channel format (for ALPHA_BPP == 4)
*
* For each pixel, 4bit of alpha information is stored in a byte-stream,
* so two pixels are packed into one byte.
@@ -485,11 +355,15 @@ void lcd_mono_bitmap(const unsigned char *src, int x, int y, int width, int heig
* so lcd_bmp() do expect even rows.
*/
-#define ALPHA_COLOR_FONT_DEPTH 2
-#define ALPHA_COLOR_LOOKUP_SHIFT (1 << ALPHA_COLOR_FONT_DEPTH)
-#define ALPHA_COLOR_LOOKUP_SIZE ((1 << ALPHA_COLOR_LOOKUP_SHIFT) - 1)
-#define ALPHA_COLOR_PIXEL_PER_BYTE (8 >> ALPHA_COLOR_FONT_DEPTH)
-#define ALPHA_COLOR_PIXEL_PER_WORD (32 >> ALPHA_COLOR_FONT_DEPTH)
+#define ALPHA_BPP 4
+#define ALPHA_MASK ((1 << ALPHA_BPP) - 1)
+#define ALPHA_PIXELS_PER_BYTE (CHAR_BIT / ALPHA_BPP)
+
+#define ALPHA_WORD_T uint32_t
+#define ALPHA_WORD_LOAD load_le32
+#define ALPHA_WORDSIZE sizeof(ALPHA_WORD_T)
+#define ALPHA_PIXELS_PER_WORD (ALPHA_WORDSIZE * CHAR_BIT / ALPHA_BPP)
+
#ifdef CPU_ARM
#define BLEND_INIT do {} while (0)
#define BLEND_FINISH do {} while(0)
@@ -498,6 +372,7 @@ void lcd_mono_bitmap(const unsigned char *src, int x, int y, int width, int heig
#define BLEND_CONT(acc, color, alpha) \
asm volatile("mla %0, %1, %2, %0" : "+&r" (acc) : "r" (color), "r" (alpha))
#define BLEND_OUT(acc) do {} while (0)
+
#elif defined(CPU_COLDFIRE)
#define ALPHA_BITMAP_READ_WORDS
#define BLEND_INIT \
@@ -509,6 +384,7 @@ void lcd_mono_bitmap(const unsigned char *src, int x, int y, int width, int heig
asm volatile("mac.l %0, %1, %%acc0" :: "%d" (color), "d" (alpha))
#define BLEND_CONT BLEND_START
#define BLEND_OUT(acc) asm volatile("movclr.l %%acc0, %0" : "=d" (acc))
+
#else
#define BLEND_INIT do {} while (0)
#define BLEND_FINISH do {} while(0)
@@ -520,7 +396,7 @@ void lcd_mono_bitmap(const unsigned char *src, int x, int y, int width, int heig
/* Blend the given two colors */
static inline unsigned blend_two_colors(unsigned c1, unsigned c2, unsigned a)
{
- a += a >> (ALPHA_COLOR_LOOKUP_SHIFT - 1);
+ a += a >> (ALPHA_BPP - 1);
#if (LCD_PIXELFORMAT == RGB565SWAPPED)
c1 = swap16(c1);
c2 = swap16(c2);
@@ -529,9 +405,9 @@ static inline unsigned blend_two_colors(unsigned c1, unsigned c2, unsigned a)
unsigned c2l = (c2 | (c2 << 16)) & 0x07e0f81f;
unsigned p;
BLEND_START(p, c1l, a);
- BLEND_CONT(p, c2l, ALPHA_COLOR_LOOKUP_SIZE + 1 - a);
+ BLEND_CONT(p, c2l, ALPHA_MASK + 1 - a);
BLEND_OUT(p);
- p = (p >> ALPHA_COLOR_LOOKUP_SHIFT) & 0x07e0f81f;
+ p = (p >> ALPHA_BPP) & 0x07e0f81f;
p |= (p >> 16);
#if (LCD_PIXELFORMAT == RGB565SWAPPED)
return swap16(p);
@@ -540,301 +416,241 @@ static inline unsigned blend_two_colors(unsigned c1, unsigned c2, unsigned a)
#endif
}
-/* Blend an image with an alpha channel
- * if image is NULL, drawing will happen according to the drawmode
- * src is the alpha channel (4bit per pixel) */
-static void ICODE_ATTR lcd_alpha_bitmap_part_mix(const fb_data* image,
- const unsigned char *src, int src_x,
- int src_y, int x, int y,
- int width, int height,
- int stride_image, int stride_src)
+static void ICODE_ATTR lcd_alpha_bitmap_part_mix(
+ const fb_data* image, const unsigned char *alpha,
+ int src_x, int src_y,
+ int x, int y, int width, int height,
+ int stride_image, int stride_alpha)
{
- fb_data *dst, *dst_row;
- unsigned dmask = 0x00000000;
- int drmode = lcd_current_viewport->drawmode;
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= lcd_current_viewport->width) ||
- (y >= lcd_current_viewport->height) || (x + width <= 0) || (y + height <= 0))
- return;
- /* initialize blending */
- BLEND_INIT;
+ struct viewport *vp = lcd_current_viewport;
+ unsigned int dmask = 0;
+ int drmode = vp->drawmode;
+ fb_data *dst;
+#ifdef ALPHA_BITMAP_READ_WORDS
+ ALPHA_WORD_T alpha_data, *alpha_word;
+ size_t alpha_offset = 0, alpha_pixels;
+#else
+ unsigned char alpha_data;
+ size_t alpha_pixels;
+#endif
- /* clipping */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > lcd_current_viewport->width)
- width = lcd_current_viewport->width - x;
- if (y + height > lcd_current_viewport->height)
- height = lcd_current_viewport->height - y;
-
- /* adjust for viewport */
- x += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
- {
- BLEND_FINISH;
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, &src_x, &src_y))
return;
- }
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
-
- /* the following drawmode combinations are possible:
- * 1) COMPLEMENT: just negates the framebuffer contents
- * 2) BG and BG+backdrop: draws _only_ background pixels with either
- * the background color or the backdrop (if any). The backdrop
- * is an image in native lcd format
- * 3) FG and FG+image: draws _only_ foreground pixels with either
- * the foreground color or an image buffer. The image is in
- * native lcd format
- * 4) SOLID, SOLID+backdrop, SOLID+image, SOLID+backdrop+image, i.e. all
- * possible combinations of 2) and 3). Draws both, fore- and background,
- * pixels. The rules of 2) and 3) apply.
- *
- * INVERSEVID swaps fore- and background pixels, i.e. background pixels
- * become foreground ones and vice versa.
- */
if (drmode & DRMODE_INVERSEVID)
{
- dmask = 0xffffffff;
- drmode &= DRMODE_SOLID; /* mask out inversevid */
+ dmask = 0xFFFFFFFFu;
+ drmode &= ~DRMODE_INVERSEVID;
}
- /* Use extra bits to avoid if () in the switch-cases below */
if (image != NULL)
drmode |= DRMODE_INT_IMG;
if ((drmode & DRMODE_BG) && lcd_backdrop)
drmode |= DRMODE_INT_BD;
- dst_row = FBADDR(x, y);
-
- int col, row = height;
- unsigned data, pixels;
- unsigned skip_end = (stride_src - width);
- unsigned skip_start = src_y * stride_src + src_x;
- unsigned skip_start_image = STRIDE_MAIN(src_y * stride_image + src_x,
- src_x * stride_image + src_y);
-
#ifdef ALPHA_BITMAP_READ_WORDS
- uint32_t *src_w = (uint32_t *)((uintptr_t)src & ~3);
- skip_start += ALPHA_COLOR_PIXEL_PER_BYTE * ((uintptr_t)src & 3);
- src_w += skip_start / ALPHA_COLOR_PIXEL_PER_WORD;
- data = letoh32(*src_w++) ^ dmask;
- pixels = skip_start % ALPHA_COLOR_PIXEL_PER_WORD;
+#define INIT_ALPHA() \
+ do { \
+ alpha_offset = src_y * stride_alpha + src_x; \
+ } while(0)
+#define START_ALPHA() \
+ do { \
+ size_t __byteskip = (uintptr_t)alpha % ALPHA_WORDSIZE; \
+ size_t __byteoff = alpha_offset / ALPHA_PIXELS_PER_BYTE; \
+ alpha_word = (ALPHA_WORD_T *)ALIGN_DOWN(alpha + __byteoff, ALPHA_WORDSIZE); \
+ alpha_data = ALPHA_WORD_LOAD(alpha_word++) ^ dmask; \
+ alpha_pixels = ((__byteoff + __byteskip) % ALPHA_WORDSIZE) * ALPHA_PIXELS_PER_BYTE; \
+ alpha_pixels += alpha_offset % ALPHA_PIXELS_PER_BYTE; \
+ alpha_data >>= alpha_pixels * ALPHA_BPP; \
+ alpha_pixels = ALPHA_PIXELS_PER_WORD - alpha_pixels; \
+ } while(0)
+#define END_ALPHA() \
+ do { \
+ alpha_offset += stride_alpha; \
+ } while(0)
+#define READ_ALPHA() \
+ ({ \
+ if (alpha_pixels == 0) { \
+ alpha_data = ALPHA_WORD_LOAD(alpha_word++) ^ dmask; \
+ alpha_pixels = ALPHA_PIXELS_PER_WORD; \
+ } \
+ ALPHA_WORD_T __ret = alpha_data & ALPHA_MASK; \
+ alpha_data >>= ALPHA_BPP; \
+ alpha_pixels--; \
+ __ret; \
+ })
+#elif ALPHA_BPP == 4
+#define INIT_ALPHA() \
+ do { \
+ alpha_pixels = src_y * stride_alpha + src_x; \
+ stride_alpha = stride_alpha - width; \
+ alpha += alpha_pixels / ALPHA_PIXELS_PER_BYTE; \
+ alpha_pixels &= 1; \
+ if (alpha_pixels) { \
+ alpha_data = *alpha++ ^ dmask; \
+ alpha_data >>= ALPHA_BPP; \
+ } \
+ } while(0)
+#define START_ALPHA() do { } while(0)
+#define END_ALPHA() \
+ do { \
+ if (stride_alpha) { \
+ alpha_pixels = stride_alpha - alpha_pixels; \
+ alpha += alpha_pixels / ALPHA_PIXELS_PER_BYTE; \
+ alpha_pixels &= 1; \
+ if (alpha_pixels) { \
+ alpha_data = *alpha++ ^ dmask; \
+ alpha_data >>= ALPHA_BPP; \
+ } \
+ } \
+ } while(0)
+#define READ_ALPHA() \
+ ({ \
+ if (alpha_pixels == 0) \
+ alpha_data = *alpha++ ^ dmask; \
+ unsigned char __ret = alpha_data & ALPHA_MASK; \
+ alpha_data >>= ALPHA_BPP; \
+ alpha_pixels ^= 1; \
+ __ret; \
+ })
#else
- src += skip_start / ALPHA_COLOR_PIXEL_PER_BYTE;
- data = *src ^ dmask;
- pixels = skip_start % ALPHA_COLOR_PIXEL_PER_BYTE;
-#endif
- data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT;
-#ifdef ALPHA_BITMAP_READ_WORDS
- pixels = 8 - pixels;
+#define INIT_ALPHA() \
+ do { \
+ alpha_pixels = src_y * stride_alpha + src_x; \
+ stride_alpha = stride_alpha - width; \
+ alpha += alpha_pixels / ALPHA_PIXELS_PER_BYTE; \
+ alpha_data = *alpha++ ^ dmask; \
+ alpha_pixels %= ALPHA_PIXELS_PER_BYTE; \
+ alpha_data >>= ALPHA_BPP * alpha_pixels; \
+ alpha_pixels = ALPHA_PIXELS_PER_BYTE - alpha_pixels; \
+ } while(0)
+#define START_ALPHA() do { } while(0)
+#define END_ALPHA() \
+ do { \
+ if ((size_t)stride_alpha <= alpha_pixels) \
+ alpha_pixels -= stride_alpha; \
+ else { \
+ alpha_pixels = stride_alpha - alpha_pixels; \
+ alpha += alpha_pixels / ALPHA_PIXELS_PER_BYTE; \
+ alpha_data = *alpha++ ^ dmask; \
+ alpha_pixels %= ALPHA_PIXELS_PER_BYTE; \
+ alpha_data >>= ALPHA_BPP * alpha_pixels; \
+ alpha_pixels = ALPHA_PIXELS_PER_BYTE - alpha_pixels; \
+ } \
+ } while(0)
+#define READ_ALPHA() \
+ ({ \
+ if (alpha_pixels == 0) { \
+ alpha_data = *alpha++ ^ dmask; \
+ alpha_pixels = ALPHA_PIXELS_PER_BYTE; \
+ } \
+ unsigned char __ret = alpha_data & ALPHA_MASK; \
+ alpha_data >>= ALPHA_BPP; \
+ alpha_pixels--; \
+ __ret; \
+ })
#endif
- /* image is only accessed in DRMODE_INT_IMG cases, i.e. when non-NULL.
- * Therefore NULL accesses are impossible and we can increment
- * unconditionally (applies for stride at the end of the loop as well) */
- image += skip_start_image;
- /* go through the rows and update each pixel */
+ dst = FBADDR(x, y);
+ image += STRIDE_MAIN(src_y * stride_image + src_x,
+ src_x * stride_image + src_y);
+
+ INIT_ALPHA();
+ BLEND_INIT;
+
do
{
- /* saving lcd_current_viewport->fg/bg_pattern and lcd_backdrop_offset into these
- * temp vars just before the loop helps gcc to opimize the loop better
- * (testing showed ~15% speedup) */
- unsigned fg, bg;
- ptrdiff_t bo, img_offset;
- col = width;
- dst = dst_row;
- dst_row += ROW_INC;
-#ifdef ALPHA_BITMAP_READ_WORDS
-#define UPDATE_SRC_ALPHA do { \
- if (--pixels) \
- data >>= ALPHA_COLOR_LOOKUP_SHIFT; \
- else \
- { \
- data = letoh32(*src_w++) ^ dmask; \
- pixels = ALPHA_COLOR_PIXEL_PER_WORD; \
- } \
- } while (0)
-#elif ALPHA_COLOR_PIXEL_PER_BYTE == 2
-#define UPDATE_SRC_ALPHA do { \
- if (pixels ^= 1) \
- data >>= ALPHA_COLOR_LOOKUP_SHIFT; \
- else \
- data = *(++src) ^ dmask; \
- } while (0)
-#else
-#define UPDATE_SRC_ALPHA do { \
- if (pixels = (++pixels % ALPHA_COLOR_PIXEL_PER_BYTE)) \
- data >>= ALPHA_COLOR_LOOKUP_SHIFT; \
- else \
- data = *(++src) ^ dmask; \
- } while (0)
-#endif
+ intptr_t bo, io;
+ unsigned int fg, bg;
+ int col = width;
+ fb_data *dst_row = dst;
+
+ START_ALPHA();
switch (drmode)
{
- case DRMODE_COMPLEMENT:
- do
- {
- *dst = blend_two_colors(*dst, ~(*dst),
- data & ALPHA_COLOR_LOOKUP_SIZE );
- dst += COL_INC;
- UPDATE_SRC_ALPHA;
- }
- while (--col);
- break;
- case DRMODE_BG|DRMODE_INT_BD:
- bo = lcd_backdrop_offset;
- do
- {
- fb_data c = *(fb_data *)((uintptr_t)dst + bo);
- *dst = blend_two_colors(c, *dst, data & ALPHA_COLOR_LOOKUP_SIZE );
- dst += COL_INC;
- image += STRIDE_MAIN(1, stride_image);
- UPDATE_SRC_ALPHA;
- }
- while (--col);
- break;
- case DRMODE_BG:
- bg = lcd_current_viewport->bg_pattern;
- do
- {
- *dst = blend_two_colors(bg, *dst, data & ALPHA_COLOR_LOOKUP_SIZE );
- dst += COL_INC;
- UPDATE_SRC_ALPHA;
- }
- while (--col);
- break;
- case DRMODE_FG|DRMODE_INT_IMG:
- img_offset = image - dst;
- do
- {
- *dst = blend_two_colors(*dst, *(dst + img_offset), data & ALPHA_COLOR_LOOKUP_SIZE );
- dst += COL_INC;
- UPDATE_SRC_ALPHA;
- }
- while (--col);
- break;
- case DRMODE_FG:
- fg = lcd_current_viewport->fg_pattern;
- do
- {
- *dst = blend_two_colors(*dst, fg, data & ALPHA_COLOR_LOOKUP_SIZE );
- dst += COL_INC;
- UPDATE_SRC_ALPHA;
- }
- while (--col);
- break;
- case DRMODE_SOLID|DRMODE_INT_BD:
- bo = lcd_backdrop_offset;
- fg = lcd_current_viewport->fg_pattern;
- do
- {
- fb_data *c = (fb_data *)((uintptr_t)dst + bo);
- *dst = blend_two_colors(*c, fg, data & ALPHA_COLOR_LOOKUP_SIZE );
- dst += COL_INC;
- UPDATE_SRC_ALPHA;
- }
- while (--col);
- break;
- case DRMODE_SOLID|DRMODE_INT_IMG:
- bg = lcd_current_viewport->bg_pattern;
- img_offset = image - dst;
- do
- {
- *dst = blend_two_colors(bg, *(dst + img_offset), data & ALPHA_COLOR_LOOKUP_SIZE );
- dst += COL_INC;
- UPDATE_SRC_ALPHA;
- }
- while (--col);
- break;
- case DRMODE_SOLID|DRMODE_INT_BD|DRMODE_INT_IMG:
- bo = lcd_backdrop_offset;
- img_offset = image - dst;
- do
- {
- fb_data *c = (fb_data *)((uintptr_t)dst + bo);
- *dst = blend_two_colors(*c, *(dst + img_offset), data & ALPHA_COLOR_LOOKUP_SIZE );
- dst += COL_INC;
- UPDATE_SRC_ALPHA;
- }
- while (--col);
- break;
- case DRMODE_SOLID:
- bg = lcd_current_viewport->bg_pattern;
- fg = lcd_current_viewport->fg_pattern;
- do
- {
- *dst = blend_two_colors(bg, fg, data & ALPHA_COLOR_LOOKUP_SIZE );
- dst += COL_INC;
- UPDATE_SRC_ALPHA;
- }
- while (--col);
- break;
- }
-#ifdef ALPHA_BITMAP_READ_WORDS
- if (skip_end < pixels)
- {
- pixels -= skip_end;
- data >>= skip_end * ALPHA_COLOR_LOOKUP_SHIFT;
- } else {
- pixels = skip_end - pixels;
- src_w += pixels / ALPHA_COLOR_PIXEL_PER_WORD;
- pixels %= ALPHA_COLOR_PIXEL_PER_WORD;
- data = letoh32(*src_w++) ^ dmask;
- data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT;
- pixels = 8 - pixels;
- }
-#else
- if (skip_end)
- {
- pixels += skip_end;
- if (pixels >= ALPHA_COLOR_PIXEL_PER_BYTE)
+ case DRMODE_COMPLEMENT:
+ do
+ {
+ *dst = blend_two_colors(*dst, ~(*dst), READ_ALPHA());
+ dst += COL_INC;
+ } while (--col);
+ break;
+ case DRMODE_BG|DRMODE_INT_BD:
+ bo = lcd_backdrop_offset;
+ do
+ {
+ *dst = blend_two_colors(*PTR_ADD(dst, bo), *dst, READ_ALPHA());
+ dst += COL_INC;
+ } while (--col);
+ break;
+ case DRMODE_BG:
+ bg = vp->bg_pattern;
+ do
+ {
+ *dst = blend_two_colors(bg, *dst, READ_ALPHA());
+ dst += COL_INC;
+ } while (--col);
+ break;
+ case DRMODE_FG|DRMODE_INT_IMG:
+ io = image - dst;
+ do
+ {
+ *dst = blend_two_colors(*dst, *(dst + io), READ_ALPHA());
+ dst += COL_INC;
+ } while (--col);
+ break;
+ case DRMODE_FG:
+ fg = vp->fg_pattern;
+ do
{
- src += pixels / ALPHA_COLOR_PIXEL_PER_BYTE;
- pixels %= ALPHA_COLOR_PIXEL_PER_BYTE;
- data = *src ^ dmask;
- data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT;
- } else
- data >>= skip_end * ALPHA_COLOR_LOOKUP_SHIFT;
+ *dst = blend_two_colors(*dst, fg, READ_ALPHA());
+ dst += COL_INC;
+ } while (--col);
+ break;
+ case DRMODE_SOLID|DRMODE_INT_BD:
+ fg = vp->fg_pattern;
+ bo = lcd_backdrop_offset;
+ do
+ {
+ *dst = blend_two_colors(*PTR_ADD(dst, bo), fg, READ_ALPHA());
+ dst += COL_INC;
+ } while (--col);
+ break;
+ case DRMODE_SOLID|DRMODE_INT_IMG:
+ bg = vp->bg_pattern;
+ io = image - dst;
+ do
+ {
+ *dst = blend_two_colors(bg, *(dst + io), READ_ALPHA());
+ dst += COL_INC;
+ } while (--col);
+ break;
+ case DRMODE_SOLID|DRMODE_INT_BD|DRMODE_INT_IMG:
+ bo = lcd_backdrop_offset;
+ io = image - dst;
+ do
+ {
+ *dst = blend_two_colors(*PTR_ADD(dst, bo), *(dst + io), READ_ALPHA());
+ dst += COL_INC;
+ } while (--col);
+ break;
+ case DRMODE_SOLID:
+ fg = vp->fg_pattern;
+ bg = vp->bg_pattern;
+ do
+ {
+ *dst = blend_two_colors(bg, fg, READ_ALPHA());
+ dst += COL_INC;
+ } while (--col);
+ break;
}
-#endif
- image += STRIDE_MAIN(stride_image,1);
- } while (--row);
+ END_ALPHA();
+ image += STRIDE_MAIN(stride_image, 1);
+ dst = dst_row + ROW_INC;
+ } while (--height);
BLEND_FINISH;
}
diff --git a/firmware/drivers/lcd-16bit-vert.c b/firmware/drivers/lcd-16bit-vert.c
index 4422fdea50..d4ad218d14 100644
--- a/firmware/drivers/lcd-16bit-vert.c
+++ b/firmware/drivers/lcd-16bit-vert.c
@@ -54,61 +54,25 @@ static void ICODE_ATTR lcd_alpha_bitmap_part_mix(const fb_data* image,
int stride_image, int stride_src);
#include "lcd-color-common.c"
-#include "lcd-16bit-common.c"
#include "lcd-bitmap-common.c"
+#include "lcd-16bit-common.c"
/*** drawing functions ***/
/* Draw a horizontal line (optimised) */
void lcd_hline(int x1, int x2, int y)
{
- int x;
+ struct viewport *vp = lcd_current_viewport;
fb_data *dst, *dst_end;
int stride_dst;
- lcd_fastpixelfunc_type *pfunc = lcd_fastpixelfuncs[lcd_current_viewport->drawmode];
-
- /* direction flip */
- if (x2 < x1)
- {
- x = x1;
- x1 = x2;
- x2 = x;
- }
-
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if (((unsigned)y >= (unsigned)lcd_current_viewport->height) ||
- (x1 >= lcd_current_viewport->width) ||
- (x2 < 0))
- return;
-
- if (x1 < 0)
- x1 = 0;
- if (x2 >= lcd_current_viewport->width)
- x2 = lcd_current_viewport->width-1;
-
- /* Adjust x1 and y to viewport */
- x1 += lcd_current_viewport->x;
- x2 += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
+ lcd_fastpixelfunc_type *pfunc = lcd_fastpixelfuncs[vp->drawmode];
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if (((unsigned)y >= (unsigned) LCD_HEIGHT) || (x1 >= LCD_WIDTH)
- || (x2 < 0))
+ if (!clip_viewport_hline(vp, &x1, &x2, &y))
return;
- /* clipping */
- if (x1 < 0)
- x1 = 0;
- if (x2 >= LCD_WIDTH)
- x2 = LCD_WIDTH-1;
-#endif
-
- dst = FBADDR(x1 , y );
- stride_dst = lcd_current_viewport->buffer->stride;
+ dst = FBADDR(x1, y);
+ stride_dst = vp->buffer->stride;
dst_end = dst + (x2 - x1) * stride_dst;
do
@@ -122,61 +86,26 @@ void lcd_hline(int x1, int x2, int y)
/* Draw a vertical line (optimised) */
void lcd_vline(int x, int y1, int y2)
{
- int y, height;
+ struct viewport *vp = lcd_current_viewport;
+ int height;
unsigned bits = 0;
enum fill_opt fillopt = OPT_NONE;
fb_data *dst, *dst_end;
- /* direction flip */
- if (y2 < y1)
- {
- y = y1;
- y1 = y2;
- y2 = y;
- }
-
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if (((unsigned)x >= (unsigned)lcd_current_viewport->width) ||
- (y1 >= lcd_current_viewport->height) ||
- (y2 < 0))
- return;
-
- if (y1 < 0)
- y1 = 0;
- if (y2 >= lcd_current_viewport->height)
- y2 = lcd_current_viewport->height-1;
-
- /* adjust for viewport */
- x += lcd_current_viewport->x;
- y1 += lcd_current_viewport->y;
- y2 += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if (( (unsigned) x >= (unsigned)LCD_WIDTH) || (y1 >= LCD_HEIGHT)
- || (y2 < 0))
+ if(!clip_viewport_vline(vp, &x, &y1, &y2))
return;
- /* clipping */
- if (y1 < 0)
- y1 = 0;
- if (y2 >= LCD_HEIGHT)
- y2 = LCD_HEIGHT-1;
-#endif
-
height = y2 - y1 + 1;
/* drawmode and optimisation */
- if (lcd_current_viewport->drawmode & DRMODE_INVERSEVID)
+ if (vp->drawmode & DRMODE_INVERSEVID)
{
- if (lcd_current_viewport->drawmode & DRMODE_BG)
+ if (vp->drawmode & DRMODE_BG)
{
if (!lcd_backdrop)
{
fillopt = OPT_SET;
- bits = lcd_current_viewport->bg_pattern;
+ bits = vp->bg_pattern;
}
else
fillopt = OPT_COPY;
@@ -184,13 +113,13 @@ void lcd_vline(int x, int y1, int y2)
}
else
{
- if (lcd_current_viewport->drawmode & DRMODE_FG)
+ if (vp->drawmode & DRMODE_FG)
{
fillopt = OPT_SET;
- bits = lcd_current_viewport->fg_pattern;
+ bits = vp->fg_pattern;
}
}
- if (fillopt == OPT_NONE && lcd_current_viewport->drawmode != DRMODE_COMPLEMENT)
+ if (fillopt == OPT_NONE && vp->drawmode != DRMODE_COMPLEMENT)
return;
dst = FBADDR(x, y1);
@@ -220,65 +149,16 @@ void ICODE_ATTR lcd_bitmap_part(const fb_data *src, int src_x, int src_y,
int stride, int x, int y, int width,
int height)
{
+ struct viewport *vp = lcd_current_viewport;
fb_data *dst;
int stride_dst;
- /******************** Image in viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= lcd_current_viewport->width) ||
- (y >= lcd_current_viewport->height) || (x + width <= 0) || (y + height <= 0))
- return;
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
-
- if (x + width > lcd_current_viewport->width)
- width = lcd_current_viewport->width - x;
- if (y + height > lcd_current_viewport->height)
- height = lcd_current_viewport->height - y;
-
- /* adjust for viewport */
- x += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, &src_x, &src_y))
return;
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
-
src += stride * src_x + src_y; /* move starting point */
dst = FBADDR(x, y);
- stride_dst = lcd_current_viewport->buffer->stride;
+ stride_dst = vp->buffer->stride;
fb_data *dst_end = dst + width * stride_dst;
do
@@ -295,66 +175,16 @@ void ICODE_ATTR lcd_bitmap_transparent_part(const fb_data *src, int src_x,
int src_y, int stride, int x,
int y, int width, int height)
{
+ struct viewport *vp = lcd_current_viewport;
fb_data *dst, *dst_end;
int stride_dst;
- /******************** Image in viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= lcd_current_viewport->width) ||
- (y >= lcd_current_viewport->height) || (x + width <= 0) || (y + height <= 0))
- return;
-
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
-
- if (x + width > lcd_current_viewport->width)
- width = lcd_current_viewport->width - x;
- if (y + height > lcd_current_viewport->height)
- height = lcd_current_viewport->height - y;
-
- /* adjust for viewport */
- x += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, &src_x, &src_y))
return;
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
-
src += stride * src_x + src_y; /* move starting point */
dst = FBADDR(x, y);
- stride_dst = lcd_current_viewport->buffer->stride;
+ stride_dst = vp->buffer->stride;
dst_end = dst + width * stride_dst;
do
@@ -363,7 +193,7 @@ void ICODE_ATTR lcd_bitmap_transparent_part(const fb_data *src, int src_x,
for(i = 0;i < height;i++)
{
if (src[i] == REPLACEWITHFG_COLOR)
- dst[i] = lcd_current_viewport->fg_pattern;
+ dst[i] = vp->fg_pattern;
else if(src[i] != TRANSPARENT_COLOR)
dst[i] = src[i];
}
diff --git a/firmware/drivers/lcd-16bit.c b/firmware/drivers/lcd-16bit.c
index d6bf5a500d..fbf6700f28 100644
--- a/firmware/drivers/lcd-16bit.c
+++ b/firmware/drivers/lcd-16bit.c
@@ -62,61 +62,26 @@ static void ICODE_ATTR lcd_alpha_bitmap_part_mix(const fb_data* image,
/* Draw a horizontal line (optimised) */
void lcd_hline(int x1, int x2, int y)
{
- int x, width;
+ struct viewport *vp = lcd_current_viewport;
+ int width;
unsigned bits = 0;
enum fill_opt fillopt = OPT_NONE;
fb_data *dst, *dst_end;
- /* direction flip */
- if (x2 < x1)
- {
- x = x1;
- x1 = x2;
- x2 = x;
- }
-
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if (((unsigned)y >= (unsigned)lcd_current_viewport->height) ||
- (x1 >= lcd_current_viewport->width) ||
- (x2 < 0))
- return;
-
- if (x1 < 0)
- x1 = 0;
- if (x2 >= lcd_current_viewport->width)
- x2 = lcd_current_viewport->width-1;
-
- /* Adjust x1 and y to viewport */
- x1 += lcd_current_viewport->x;
- x2 += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if (((unsigned)y >= (unsigned) LCD_HEIGHT) || (x1 >= LCD_WIDTH)
- || (x2 < 0))
+ if (!clip_viewport_hline(vp, &x1, &x2, &y))
return;
- /* clipping */
- if (x1 < 0)
- x1 = 0;
- if (x2 >= LCD_WIDTH)
- x2 = LCD_WIDTH-1;
-#endif
-
width = x2 - x1 + 1;
/* drawmode and optimisation */
- if (lcd_current_viewport->drawmode & DRMODE_INVERSEVID)
+ if (vp->drawmode & DRMODE_INVERSEVID)
{
- if (lcd_current_viewport->drawmode & DRMODE_BG)
+ if (vp->drawmode & DRMODE_BG)
{
if (!lcd_backdrop)
{
fillopt = OPT_SET;
- bits = lcd_current_viewport->bg_pattern;
+ bits = vp->bg_pattern;
}
else
fillopt = OPT_COPY;
@@ -124,13 +89,13 @@ void lcd_hline(int x1, int x2, int y)
}
else
{
- if (lcd_current_viewport->drawmode & DRMODE_FG)
+ if (vp->drawmode & DRMODE_FG)
{
fillopt = OPT_SET;
- bits = lcd_current_viewport->fg_pattern;
+ bits = vp->fg_pattern;
}
}
- if (fillopt == OPT_NONE && lcd_current_viewport->drawmode != DRMODE_COMPLEMENT)
+ if (fillopt == OPT_NONE && vp->drawmode != DRMODE_COMPLEMENT)
return;
dst = FBADDR(x1, y);
@@ -158,52 +123,16 @@ void lcd_hline(int x1, int x2, int y)
/* Draw a vertical line (optimised) */
void lcd_vline(int x, int y1, int y2)
{
- int y;
+ struct viewport *vp = lcd_current_viewport;
fb_data *dst, *dst_end;
int stride_dst;
- lcd_fastpixelfunc_type *pfunc = lcd_fastpixelfuncs[lcd_current_viewport->drawmode];
-
- /* direction flip */
- if (y2 < y1)
- {
- y = y1;
- y1 = y2;
- y2 = y;
- }
+ lcd_fastpixelfunc_type *pfunc = lcd_fastpixelfuncs[vp->drawmode];
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if (((unsigned)x >= (unsigned)lcd_current_viewport->width) ||
- (y1 >= lcd_current_viewport->height) ||
- (y2 < 0))
+ if (!clip_viewport_vline(vp, &x, &y1, &y2))
return;
- if (y1 < 0)
- y1 = 0;
- if (y2 >= lcd_current_viewport->height)
- y2 = lcd_current_viewport->height-1;
-
- /* adjust for viewport */
- x += lcd_current_viewport->x;
- y1 += lcd_current_viewport->y;
- y2 += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if (( (unsigned) x >= (unsigned)LCD_WIDTH) || (y1 >= LCD_HEIGHT)
- || (y2 < 0))
- return;
-
- /* clipping */
- if (y1 < 0)
- y1 = 0;
- if (y2 >= LCD_HEIGHT)
- y2 = LCD_HEIGHT-1;
-#endif
-
- dst = FBADDR(x , y1);
- stride_dst = lcd_current_viewport->buffer->stride;
+ dst = FBADDR(x, y1);
+ stride_dst = vp->buffer->stride;
dst_end = dst + (y2 - y1) * stride_dst;
do
@@ -219,66 +148,16 @@ void ICODE_ATTR lcd_bitmap_part(const fb_data *src, int src_x, int src_y,
int stride, int x, int y, int width,
int height)
{
+ struct viewport *vp = lcd_current_viewport;
fb_data *dst;
int stride_dst;
- /******************** Image in viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= lcd_current_viewport->width) ||
- (y >= lcd_current_viewport->height) || (x + width <= 0) || (y + height <= 0))
- return;
-
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
-
- if (x + width > lcd_current_viewport->width)
- width = lcd_current_viewport->width - x;
- if (y + height > lcd_current_viewport->height)
- height = lcd_current_viewport->height - y;
-
- /* adjust for viewport */
- x += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, &src_x, &src_y))
return;
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
-
src += stride * src_y + src_x; /* move starting point */
dst = FBADDR(x, y);
- stride_dst = lcd_current_viewport->buffer->stride;
+ stride_dst = vp->buffer->stride;
do
{
@@ -294,64 +173,14 @@ void ICODE_ATTR lcd_bitmap_transparent_part(const fb_data *src, int src_x,
int src_y, int stride, int x,
int y, int width, int height)
{
+ struct viewport *vp = lcd_current_viewport;
fb_data *dst;
- unsigned fg = lcd_current_viewport->fg_pattern;
- int stride_dst = lcd_current_viewport->buffer->stride;
-
- /******************** Image in viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= lcd_current_viewport->width) ||
- (y >= lcd_current_viewport->height) || (x + width <= 0) || (y + height <= 0))
- return;
-
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
-
- if (x + width > lcd_current_viewport->width)
- width = lcd_current_viewport->width - x;
- if (y + height > lcd_current_viewport->height)
- height = lcd_current_viewport->height - y;
-
- /* adjust for viewport */
- x += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
+ unsigned fg = vp->fg_pattern;
+ int stride_dst = vp->buffer->stride;
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, &src_x, &src_y))
return;
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
-
src += stride * src_y + src_x; /* move starting point */
dst = FBADDR(x, y);
diff --git a/firmware/drivers/lcd-1bit-vert.c b/firmware/drivers/lcd-1bit-vert.c
index c86ced9b6c..77eb4ab2a1 100644
--- a/firmware/drivers/lcd-1bit-vert.c
+++ b/firmware/drivers/lcd-1bit-vert.c
@@ -71,6 +71,7 @@ static struct viewport default_vp =
.y = 0,
.width = LCDM(WIDTH),
.height = LCDM(HEIGHT),
+ .flags = 0,
.font = FONT_SYSFIXED,
.drawmode = DRMODE_SOLID,
.buffer = NULL,
@@ -82,7 +83,7 @@ static void *LCDFN(frameaddress_default)(int x, int y)
{
/* the default expects a buffer the same size as the screen */
struct frame_buffer_t *fb = CURRENT_VP->buffer;
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if defined(MAIN_LCD) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
size_t element = (x * LCDM(NATIVE_STRIDE)(fb->stride)) + y;
#else
size_t element = (y * LCDM(NATIVE_STRIDE)(fb->stride)) + x;
@@ -91,6 +92,8 @@ static void *LCDFN(frameaddress_default)(int x, int y)
return fb->FBFN(ptr) + element;/*(element % fb->elems);*/
}
+#include "lcd-bitmap-common.c"
+
/* LCD init */
void LCDFN(init)(void)
{
@@ -105,43 +108,6 @@ void LCDFN(init)(void)
}
-/*** parameter handling ***/
-
-void LCDFN(set_drawmode)(int mode)
-{
- CURRENT_VP->drawmode = mode & (DRMODE_SOLID|DRMODE_INVERSEVID);
-}
-
-int LCDFN(get_drawmode)(void)
-{
- return CURRENT_VP->drawmode;
-}
-
-int LCDFN(getwidth)(void)
-{
- return CURRENT_VP->width;
-}
-
-int LCDFN(getheight)(void)
-{
- return CURRENT_VP->height;
-}
-
-void LCDFN(setfont)(int newfont)
-{
- CURRENT_VP->font = newfont;
-}
-
-int LCDFN(getfont)(void)
-{
- return CURRENT_VP->font;
-}
-
-int LCDFN(getstringsize)(const unsigned char *str, int *w, int *h)
-{
- return font_getstringsize(str, w, h, CURRENT_VP->font);
-}
-
/*** low-level drawing functions ***/
static void setpixel(int x, int y)
@@ -237,188 +203,24 @@ void LCDFN(clear_display)(void)
unsigned bits = (CURRENT_VP->drawmode & DRMODE_INVERSEVID) ? 0xFFu : 0;
memset(LCDFB(0, 0), bits, FBSIZE);
- LCDFN(scroll_info).lines = 0;
-}
-
-/* Clear the current viewport */
-void LCDFN(clear_viewport)(void)
-{
- int oldmode;
-
- if (CURRENT_VP == &default_vp &&
- default_vp.buffer == &LCDFN(framebuffer_default))
- {
- LCDFN(clear_display)();
- }
- else
- {
- oldmode = CURRENT_VP->drawmode;
-
- /* Invert the INVERSEVID bit and set basic mode to SOLID */
- CURRENT_VP->drawmode = (~CURRENT_VP->drawmode & DRMODE_INVERSEVID) |
- DRMODE_SOLID;
-
- LCDFN(fillrect)(0, 0, CURRENT_VP->width, CURRENT_VP->height);
-
- CURRENT_VP->drawmode = oldmode;
-
- LCDFN(scroll_stop_viewport)(CURRENT_VP);
- }
-
- CURRENT_VP->flags &= ~(VP_FLAG_VP_SET_CLEAN);
-}
-
-/* Set a single pixel */
-void LCDFN(drawpixel)(int x, int y)
-{
- if ( ((unsigned)x < (unsigned)CURRENT_VP->width)
- && ((unsigned)y < (unsigned)CURRENT_VP->height)
-#if defined(HAVE_VIEWPORT_CLIP)
- && ((unsigned)x < (unsigned)LCDM(WIDTH))
- && ((unsigned)y < (unsigned)LCDM(HEIGHT))
-#endif
- )
- LCDFN(pixelfuncs)[CURRENT_VP->drawmode](CURRENT_VP->x + x, CURRENT_VP->y + y);
-}
-
-/* Draw a line */
-void LCDFN(drawline)(int x1, int y1, int x2, int y2)
-{
- int numpixels;
- int i;
- int deltax, deltay;
- int d, dinc1, dinc2;
- int x, xinc1, xinc2;
- int y, yinc1, yinc2;
- LCDFN(pixelfunc_type) *pfunc = LCDFN(pixelfuncs)[CURRENT_VP->drawmode];
-
- deltax = abs(x2 - x1);
- if (deltax == 0)
- {
- /* DEBUGF(LCDNAME "drawline() called for vertical line - optimisation.\n"); */
- LCDFN(vline)(x1, y1, y2);
- return;
- }
- deltay = abs(y2 - y1);
- if (deltay == 0)
- {
- /* DEBUGF(LCDNAME "drawline() called for horizontal line - optimisation.\n"); */
- LCDFN(hline)(x1, x2, y1);
- return;
- }
- xinc2 = 1;
- yinc2 = 1;
-
- if (deltax >= deltay)
- {
- numpixels = deltax;
- d = 2 * deltay - deltax;
- dinc1 = deltay * 2;
- dinc2 = (deltay - deltax) * 2;
- xinc1 = 1;
- yinc1 = 0;
- }
- else
- {
- numpixels = deltay;
- d = 2 * deltax - deltay;
- dinc1 = deltax * 2;
- dinc2 = (deltax - deltay) * 2;
- xinc1 = 0;
- yinc1 = 1;
- }
- numpixels++; /* include endpoints */
-
- if (x1 > x2)
- {
- xinc1 = -xinc1;
- xinc2 = -xinc2;
- }
-
- if (y1 > y2)
- {
- yinc1 = -yinc1;
- yinc2 = -yinc2;
- }
-
- x = x1;
- y = y1;
-
- for (i = 0; i < numpixels; i++)
- {
- if ( ((unsigned)x < (unsigned)CURRENT_VP->width)
- && ((unsigned)y < (unsigned)CURRENT_VP->height)
-#if defined(HAVE_VIEWPORT_CLIP)
- && ((unsigned)x < (unsigned)LCDM(WIDTH))
- && ((unsigned)y < (unsigned)LCDM(HEIGHT))
-#endif
- )
- pfunc(CURRENT_VP->x + x, CURRENT_VP->y + y);
-
- if (d < 0)
- {
- d += dinc1;
- x += xinc1;
- y += yinc1;
- }
- else
- {
- d += dinc2;
- x += xinc2;
- y += yinc2;
- }
- }
+ LCDFN(scroll_stop)();
}
/* Draw a horizontal line (optimised) */
void LCDFN(hline)(int x1, int x2, int y)
{
- int x, width;
+ struct viewport *vp = CURRENT_VP;
+ int width;
unsigned char *dst, *dst_end;
unsigned mask;
LCDFN(blockfunc_type) *bfunc;
- /* direction flip */
- if (x2 < x1)
- {
- x = x1;
- x1 = x2;
- x2 = x;
- }
-
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if (((unsigned)y >= (unsigned)CURRENT_VP->height) || (x1 >= CURRENT_VP->width)
- || (x2 < 0))
- return;
-
- if (x1 < 0)
- x1 = 0;
- if (x2 >= CURRENT_VP->width)
- x2 = CURRENT_VP->width-1;
-
- /* adjust to viewport */
- x1 += CURRENT_VP->x;
- x2 += CURRENT_VP->x;
- y += CURRENT_VP->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if (((unsigned)y >= (unsigned) LCDM(HEIGHT)) || (x1 >= LCDM(WIDTH))
- || (x2 < 0))
+ if (!clip_viewport_hline(vp, &x1, &x2, &y))
return;
- /* clipping */
- if (x1 < 0)
- x1 = 0;
- if (x2 >= LCDM(WIDTH))
- x2 = LCDM(WIDTH)-1;
-#endif
-
width = x2 - x1 + 1;
- bfunc = LCDFN(blockfuncs)[CURRENT_VP->drawmode];
+ bfunc = LCDFN(blockfuncs)[vp->drawmode];
dst = LCDFB(x1,y>>3);
mask = BIT_N(y & 7);
@@ -431,56 +233,22 @@ void LCDFN(hline)(int x1, int x2, int y)
/* Draw a vertical line (optimised) */
void LCDFN(vline)(int x, int y1, int y2)
{
+ struct viewport *vp = CURRENT_VP;
int ny;
FBFN(data) *dst;
int stride_dst;
unsigned mask, mask_bottom;
LCDFN(blockfunc_type) *bfunc;
- /* direction flip */
- if (y2 < y1)
- {
- ny = y1;
- y1 = y2;
- y2 = ny;
- }
-
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if (((unsigned)x >= (unsigned)CURRENT_VP->width) || (y1 >= CURRENT_VP->height)
- || (y2 < 0))
- return;
-
- if (y1 < 0)
- y1 = 0;
- if (y2 >= CURRENT_VP->height)
- y2 = CURRENT_VP->height-1;
-
- /* adjust for viewport */
- y1 += CURRENT_VP->y;
- y2 += CURRENT_VP->y;
- x += CURRENT_VP->x;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if (( (unsigned) x >= (unsigned)LCDM(WIDTH)) || (y1 >= LCDM(HEIGHT))
- || (y2 < 0))
+ if (!clip_viewport_vline(vp, &x, &y1, &y2))
return;
- /* clipping */
- if (y1 < 0)
- y1 = 0;
- if (y2 >= LCDM(HEIGHT))
- y2 = LCDM(HEIGHT)-1;
-#endif
-
- bfunc = LCDFN(blockfuncs)[CURRENT_VP->drawmode];
+ bfunc = LCDFN(blockfuncs)[vp->drawmode];
dst = LCDFB(x,y1>>3);
ny = y2 - (y1 & ~7);
mask = 0xFFu << (y1 & 7);
mask_bottom = 0xFFu >> (~ny & 7);
- stride_dst = CURRENT_VP->buffer->stride;
+ stride_dst = vp->buffer->stride;
for (; ny >= 8; ny -= 8)
{
@@ -492,24 +260,10 @@ void LCDFN(vline)(int x, int y1, int y2)
bfunc(dst, mask, 0xFFu);
}
-/* Draw a rectangular box */
-void LCDFN(drawrect)(int x, int y, int width, int height)
-{
- if ((width <= 0) || (height <= 0))
- return;
-
- int x2 = x + width - 1;
- int y2 = y + height - 1;
-
- LCDFN(vline)(x, y, y2);
- LCDFN(vline)(x2, y, y2);
- LCDFN(hline)(x, x2, y);
- LCDFN(hline)(x, x2, y2);
-}
-
/* Fill a rectangular area */
void LCDFN(fillrect)(int x, int y, int width, int height)
{
+ struct viewport *vp = CURRENT_VP;
int ny;
FBFN(data) *dst, *dst_end;
int stride_dst;
@@ -518,76 +272,30 @@ void LCDFN(fillrect)(int x, int y, int width, int height)
LCDFN(blockfunc_type) *bfunc;
bool fillopt = false;
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= CURRENT_VP->width)
- || (y >= CURRENT_VP->height) || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, NULL, NULL))
return;
- if (x < 0)
- {
- width += x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- y = 0;
- }
- if (x + width > CURRENT_VP->width)
- width = CURRENT_VP->width - x;
- if (y + height > CURRENT_VP->height)
- height = CURRENT_VP->height - y;
-
- /* adjust for viewport */
- x += CURRENT_VP->x;
- y += CURRENT_VP->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCDM(WIDTH)) || (y >= LCDM(HEIGHT))
- || (x + width <= 0) || (y + height <= 0))
- return;
-
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- y = 0;
- }
- if (x + width > LCDM(WIDTH))
- width = LCDM(WIDTH) - x;
- if (y + height > LCDM(HEIGHT))
- height = LCDM(HEIGHT) - y;
-#endif
-
- if (CURRENT_VP->drawmode & DRMODE_INVERSEVID)
+ if (vp->drawmode & DRMODE_INVERSEVID)
{
- if (CURRENT_VP->drawmode & DRMODE_BG)
+ if (vp->drawmode & DRMODE_BG)
{
fillopt = true;
}
}
else
{
- if (CURRENT_VP->drawmode & DRMODE_FG)
+ if (vp->drawmode & DRMODE_FG)
{
fillopt = true;
bits = 0xFFu;
}
}
- bfunc = LCDFN(blockfuncs)[CURRENT_VP->drawmode];
+ bfunc = LCDFN(blockfuncs)[vp->drawmode];
dst = LCDFB(x,y>>3);
ny = height - 1 + (y & 7);
mask = 0xFFu << (y & 7);
mask_bottom = 0xFFu >> (~ny & 7);
- stride_dst = CURRENT_VP->buffer->stride;
+ stride_dst = vp->buffer->stride;
for (; ny >= 8; ny -= 8)
{
@@ -635,81 +343,31 @@ void ICODE_ATTR LCDFN(bitmap_part)(const unsigned char *src, int src_x,
int src_y, int stride, int x, int y,
int width, int height)
{
+ struct viewport *vp = CURRENT_VP;
int shift, ny;
FBFN(data) *dst, *dst_end;
int stride_dst;
unsigned mask, mask_bottom;
LCDFN(blockfunc_type) *bfunc;
- /******************** Image in viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= CURRENT_VP->width)
- || (y >= CURRENT_VP->height) || (x + width <= 0) || (y + height <= 0))
- return;
-
- /* clip image in viewport */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > CURRENT_VP->width)
- width = CURRENT_VP->width - x;
- if (y + height > CURRENT_VP->height)
- height = CURRENT_VP->height - y;
-
- /* adjust for viewport */
- x += CURRENT_VP->x;
- y += CURRENT_VP->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCDM(WIDTH)) || (y >= LCDM(HEIGHT))
- || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, &src_x, &src_y))
return;
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > LCDM(WIDTH))
- width = LCDM(WIDTH) - x;
- if (y + height > LCDM(HEIGHT))
- height = LCDM(HEIGHT) - y;
-#endif
-
src += stride * (src_y >> 3) + src_x; /* move starting point */
src_y &= 7;
y -= src_y;
dst = LCDFB(x,y>>3);
- stride_dst = CURRENT_VP->buffer->stride;
+ stride_dst = vp->buffer->stride;
shift = y & 7;
ny = height - 1 + shift + src_y;
- bfunc = LCDFN(blockfuncs)[CURRENT_VP->drawmode];
+ bfunc = LCDFN(blockfuncs)[vp->drawmode];
mask = 0xFFu << (shift + src_y);
mask_bottom = 0xFFu >> (~ny & 7);
if (shift == 0)
{
- bool copyopt = (CURRENT_VP->drawmode == DRMODE_SOLID);
+ bool copyopt = (vp->drawmode == DRMODE_SOLID);
for (; ny >= 8; ny -= 8)
{
@@ -781,5 +439,3 @@ void LCDFN(bitmap)(const unsigned char *src, int x, int y, int width,
{
LCDFN(bitmap_part)(src, 0, 0, width, x, y, width, height);
}
-
-#include "lcd-bitmap-common.c"
diff --git a/firmware/drivers/lcd-24bit.c b/firmware/drivers/lcd-24bit.c
index c3aa27f7ce..dbb7e543af 100644
--- a/firmware/drivers/lcd-24bit.c
+++ b/firmware/drivers/lcd-24bit.c
@@ -61,38 +61,15 @@ static void ICODE_ATTR lcd_alpha_bitmap_part_mix(const fb_data* image,
/* Clear the current viewport */
void lcd_clear_viewport(void)
{
+ struct viewport *vp = lcd_current_viewport;
fb_data *dst, *dst_end;
int x, y, width, height;
int len, step;
- x = lcd_current_viewport->x;
- y = lcd_current_viewport->y;
- width = lcd_current_viewport->width;
- height = lcd_current_viewport->height;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
- return;
-
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
+ x = vp->x;
+ y = vp->y;
+ width = vp->width;
+ height = vp->height;
len = STRIDE_MAIN(width, height);
step = STRIDE_MAIN(ROW_INC, COL_INC);
@@ -100,9 +77,9 @@ void lcd_clear_viewport(void)
dst = FBADDR(x, y);
dst_end = FBADDR(x + width - 1 , y + height - 1);
- if (lcd_current_viewport->drawmode & DRMODE_INVERSEVID)
+ if (vp->drawmode & DRMODE_INVERSEVID)
{
- fb_data px = FB_SCALARPACK(lcd_current_viewport->fg_pattern);
+ fb_data px = FB_SCALARPACK(vp->fg_pattern);
do
{
fb_data *end = dst + len;
@@ -117,7 +94,7 @@ void lcd_clear_viewport(void)
{
if (!lcd_backdrop)
{
- fb_data px = FB_SCALARPACK(lcd_current_viewport->bg_pattern);
+ fb_data px = FB_SCALARPACK(vp->bg_pattern);
do
{
fb_data *end = dst + len;
@@ -140,12 +117,12 @@ void lcd_clear_viewport(void)
}
}
- if (lcd_current_viewport == &default_vp)
+ if (vp == &default_vp)
lcd_scroll_stop();
else
- lcd_scroll_stop_viewport(lcd_current_viewport);
+ lcd_scroll_stop_viewport(vp);
- lcd_current_viewport->flags &= ~(VP_FLAG_VP_SET_CLEAN);
+ vp->flags &= ~(VP_FLAG_VP_SET_CLEAN);
}
/*** low-level drawing functions ***/
@@ -191,70 +168,25 @@ lcd_fastpixelfunc_type* const * lcd_fastpixelfuncs = lcd_fastpixelfuncs_bgcolor;
/* Fill a rectangular area */
void lcd_fillrect(int x, int y, int width, int height)
{
+ struct viewport *vp = lcd_current_viewport;
enum fill_opt fillopt = OPT_NONE;
fb_data *dst, *dst_end;
int len, step;
fb_data bits;
memset(&bits, 0, sizeof(fb_data));
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= lcd_current_viewport->width) ||
- (y >= lcd_current_viewport->height) || (x + width <= 0) || (y + height <= 0))
- return;
-
- if (x < 0)
- {
- width += x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- y = 0;
- }
- if (x + width > lcd_current_viewport->width)
- width = lcd_current_viewport->width - x;
- if (y + height > lcd_current_viewport->height)
- height = lcd_current_viewport->height - y;
-
- /* adjust for viewport */
- x += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, NULL, NULL))
return;
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
-
/* drawmode and optimisation */
- if (lcd_current_viewport->drawmode & DRMODE_INVERSEVID)
+ if (vp->drawmode & DRMODE_INVERSEVID)
{
- if (lcd_current_viewport->drawmode & DRMODE_BG)
+ if (vp->drawmode & DRMODE_BG)
{
if (!lcd_backdrop)
{
fillopt = OPT_SET;
- bits = FB_SCALARPACK(lcd_current_viewport->bg_pattern);
+ bits = FB_SCALARPACK(vp->bg_pattern);
}
else
fillopt = OPT_COPY;
@@ -262,13 +194,13 @@ void lcd_fillrect(int x, int y, int width, int height)
}
else
{
- if (lcd_current_viewport->drawmode & DRMODE_FG)
+ if (vp->drawmode & DRMODE_FG)
{
fillopt = OPT_SET;
- bits = FB_SCALARPACK(lcd_current_viewport->fg_pattern);
+ bits = FB_SCALARPACK(vp->fg_pattern);
}
}
- if (fillopt == OPT_NONE && lcd_current_viewport->drawmode != DRMODE_COMPLEMENT)
+ if (fillopt == OPT_NONE && vp->drawmode != DRMODE_COMPLEMENT)
return;
dst = FBADDR(x, y);
@@ -329,65 +261,16 @@ void ICODE_ATTR lcd_mono_bitmap_part(const unsigned char *src, int src_x,
int src_y, int stride, int x, int y,
int width, int height)
{
+ struct viewport *vp = lcd_current_viewport;
const unsigned char *src_end;
fb_data *dst, *dst_col;
unsigned dmask = 0x100; /* bit 8 == sentinel */
- int drmode = lcd_current_viewport->drawmode;
+ int drmode = vp->drawmode;
int row;
- /******************** Image in viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= lcd_current_viewport->width) ||
- (y >= lcd_current_viewport->height) || (x + width <= 0) || (y + height <= 0))
- return;
-
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > lcd_current_viewport->width)
- width = lcd_current_viewport->width - x;
- if (y + height > lcd_current_viewport->height)
- height = lcd_current_viewport->height - y;
-
- /* adjust for viewport */
- x += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, &src_x, &src_y))
return;
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
-
src += stride * (src_y >> 3) + src_x; /* move starting point */
src_y &= 7;
src_end = src + width;
@@ -452,7 +335,7 @@ void ICODE_ATTR lcd_mono_bitmap_part(const unsigned char *src, int src_x,
break;
case DRMODE_BG:
- bg = FB_SCALARPACK(lcd_current_viewport->bg_pattern);
+ bg = FB_SCALARPACK(vp->bg_pattern);
do
{
if (!(data & 0x01))
@@ -465,7 +348,7 @@ void ICODE_ATTR lcd_mono_bitmap_part(const unsigned char *src, int src_x,
break;
case DRMODE_FG:
- fg = FB_SCALARPACK(lcd_current_viewport->fg_pattern);
+ fg = FB_SCALARPACK(vp->fg_pattern);
do
{
if (data & 0x01)
@@ -478,7 +361,7 @@ void ICODE_ATTR lcd_mono_bitmap_part(const unsigned char *src, int src_x,
break;
case DRMODE_SOLID|DRMODE_INT_BD:
- fg = FB_SCALARPACK(lcd_current_viewport->fg_pattern);
+ fg = FB_SCALARPACK(vp->fg_pattern);
bo = lcd_backdrop_offset;
do
{
@@ -491,8 +374,8 @@ void ICODE_ATTR lcd_mono_bitmap_part(const unsigned char *src, int src_x,
break;
case DRMODE_SOLID:
- fg = FB_SCALARPACK(lcd_current_viewport->fg_pattern);
- bg = FB_SCALARPACK(lcd_current_viewport->bg_pattern);
+ fg = FB_SCALARPACK(vp->fg_pattern);
+ bg = FB_SCALARPACK(vp->bg_pattern);
do
{
*dst = (data & 0x01) ? fg : bg;
@@ -562,62 +445,14 @@ static void ICODE_ATTR lcd_alpha_bitmap_part_mix(const fb_data* image,
int width, int height,
int stride_image, int stride_src)
{
+ struct viewport *vp = lcd_current_viewport;
fb_data *dst, *dst_row;
unsigned dmask = 0x00000000;
- int drmode = lcd_current_viewport->drawmode;
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= lcd_current_viewport->width) ||
- (y >= lcd_current_viewport->height) || (x + width <= 0) || (y + height <= 0))
- return;
+ int drmode = vp->drawmode;
- /* clipping */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > lcd_current_viewport->width)
- width = lcd_current_viewport->width - x;
- if (y + height > lcd_current_viewport->height)
- height = lcd_current_viewport->height - y;
-
- /* adjust for viewport */
- x += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, &src_x, &src_y))
return;
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
-
/* the following drawmode combinations are possible:
* 1) COMPLEMENT: just negates the framebuffer contents
* 2) BG and BG+backdrop: draws _only_ background pixels with either
@@ -739,7 +574,7 @@ static void ICODE_ATTR lcd_alpha_bitmap_part_mix(const fb_data* image,
while (--col);
break;
case DRMODE_BG:
- bg = lcd_current_viewport->bg_pattern;
+ bg = vp->bg_pattern;
do
{
unsigned px = FB_UNPACK_SCALAR_LCD(*dst);
@@ -762,7 +597,7 @@ static void ICODE_ATTR lcd_alpha_bitmap_part_mix(const fb_data* image,
while (--col);
break;
case DRMODE_FG:
- fg = lcd_current_viewport->fg_pattern;
+ fg = vp->fg_pattern;
do
{
unsigned px = FB_UNPACK_SCALAR_LCD(*dst);
@@ -774,7 +609,7 @@ static void ICODE_ATTR lcd_alpha_bitmap_part_mix(const fb_data* image,
break;
case DRMODE_SOLID|DRMODE_INT_BD:
bo = lcd_backdrop_offset;
- fg = lcd_current_viewport->fg_pattern;
+ fg = vp->fg_pattern;
do
{
unsigned c = FB_UNPACK_SCALAR_LCD(*(fb_data *)((uintptr_t)dst + bo));
@@ -785,7 +620,7 @@ static void ICODE_ATTR lcd_alpha_bitmap_part_mix(const fb_data* image,
while (--col);
break;
case DRMODE_SOLID|DRMODE_INT_IMG:
- bg = lcd_current_viewport->bg_pattern;
+ bg = vp->bg_pattern;
img_offset = image - dst;
do
{
@@ -810,8 +645,8 @@ static void ICODE_ATTR lcd_alpha_bitmap_part_mix(const fb_data* image,
while (--col);
break;
case DRMODE_SOLID:
- bg = lcd_current_viewport->bg_pattern;
- fg = lcd_current_viewport->fg_pattern;
+ bg = vp->bg_pattern;
+ fg = vp->fg_pattern;
do
{
*dst = blend_two_colors(bg, fg, data & ALPHA_COLOR_LOOKUP_SIZE );
@@ -858,52 +693,17 @@ static void ICODE_ATTR lcd_alpha_bitmap_part_mix(const fb_data* image,
/* Draw a horizontal line (optimised) */
void lcd_hline(int x1, int x2, int y)
{
- int x, width;
+ struct viewport *vp = lcd_current_viewport;
+ int width;
fb_data *dst, *dst_end;
- lcd_fastpixelfunc_type *pfunc = lcd_fastpixelfuncs[lcd_current_viewport->drawmode];
+ lcd_fastpixelfunc_type *pfunc = lcd_fastpixelfuncs[vp->drawmode];
- /* direction flip */
- if (x2 < x1)
- {
- x = x1;
- x1 = x2;
- x2 = x;
- }
-
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if (((unsigned)y >= (unsigned)lcd_current_viewport->height) ||
- (x1 >= lcd_current_viewport->width) ||
- (x2 < 0))
- return;
-
- if (x1 < 0)
- x1 = 0;
- if (x2 >= lcd_current_viewport->width)
- x2 = lcd_current_viewport->width-1;
-
- /* Adjust x1 and y to viewport */
- x1 += lcd_current_viewport->x;
- x2 += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if (((unsigned)y >= (unsigned) LCD_HEIGHT) || (x1 >= LCD_WIDTH)
- || (x2 < 0))
+ if (!clip_viewport_hline(vp, &x1, &x2, &y))
return;
- /* clipping */
- if (x1 < 0)
- x1 = 0;
- if (x2 >= LCD_WIDTH)
- x2 = LCD_WIDTH-1;
-#endif
-
width = x2 - x1 + 1;
- dst = FBADDR(x1 , y);
+ dst = FBADDR(x1, y);
dst_end = dst + width;
do
{
@@ -915,50 +715,14 @@ void lcd_hline(int x1, int x2, int y)
/* Draw a vertical line (optimised) */
void lcd_vline(int x, int y1, int y2)
{
- int y;
+ struct viewport *vp = lcd_current_viewport;
fb_data *dst, *dst_end;
- lcd_fastpixelfunc_type *pfunc = lcd_fastpixelfuncs[lcd_current_viewport->drawmode];
-
- /* direction flip */
- if (y2 < y1)
- {
- y = y1;
- y1 = y2;
- y2 = y;
- }
-
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if (((unsigned)x >= (unsigned)lcd_current_viewport->width) ||
- (y1 >= lcd_current_viewport->height) ||
- (y2 < 0))
- return;
+ lcd_fastpixelfunc_type *pfunc = lcd_fastpixelfuncs[vp->drawmode];
- if (y1 < 0)
- y1 = 0;
- if (y2 >= lcd_current_viewport->height)
- y2 = lcd_current_viewport->height-1;
-
- /* adjust for viewport */
- x += lcd_current_viewport->x;
- y1 += lcd_current_viewport->y;
- y2 += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if (( (unsigned) x >= (unsigned)LCD_WIDTH) || (y1 >= LCD_HEIGHT)
- || (y2 < 0))
+ if (!clip_viewport_vline(vp, &x, &y1, &y2))
return;
- /* clipping */
- if (y1 < 0)
- y1 = 0;
- if (y2 >= LCD_HEIGHT)
- y2 = LCD_HEIGHT-1;
-#endif
-
- dst = FBADDR(x , y1);
+ dst = FBADDR(x, y1);
dst_end = dst + (y2 - y1) * LCD_WIDTH;
do
@@ -974,62 +738,12 @@ void ICODE_ATTR lcd_bitmap_part(const fb_data *src, int src_x, int src_y,
int stride, int x, int y, int width,
int height)
{
+ struct viewport *vp = lcd_current_viewport;
fb_data *dst;
- /******************** Image in viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= lcd_current_viewport->width) ||
- (y >= lcd_current_viewport->height) || (x + width <= 0) || (y + height <= 0))
- return;
-
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
-
- if (x + width > lcd_current_viewport->width)
- width = lcd_current_viewport->width - x;
- if (y + height > lcd_current_viewport->height)
- height = lcd_current_viewport->height - y;
-
- /* adjust for viewport */
- x += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, &src_x, &src_y))
return;
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
-
src += stride * src_y + src_x; /* move starting point */
dst = FBADDR(x, y);
@@ -1047,69 +761,19 @@ void ICODE_ATTR lcd_bitmap_transparent_part(const fb_data *src, int src_x,
int src_y, int stride, int x,
int y, int width, int height)
{
+ struct viewport *vp = lcd_current_viewport;
fb_data *dst;
fb_data fg, transparent, replacewithfg;
- /******************** Image in viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= lcd_current_viewport->width) ||
- (y >= lcd_current_viewport->height) || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, &src_x, &src_y))
return;
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
-
- if (x + width > lcd_current_viewport->width)
- width = lcd_current_viewport->width - x;
- if (y + height > lcd_current_viewport->height)
- height = lcd_current_viewport->height - y;
-
- /* adjust for viewport */
- x += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
- return;
-
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
-
src += stride * src_y + src_x; /* move starting point */
dst = FBADDR(x, y);
transparent = FB_SCALARPACK(TRANSPARENT_COLOR);
replacewithfg = FB_SCALARPACK(REPLACEWITHFG_COLOR);
- fg = FB_SCALARPACK(lcd_current_viewport->fg_pattern);
+ fg = FB_SCALARPACK(vp->fg_pattern);
#define CMP(c1, c2) (c1.r == c2.r && c1.g == c2.g && c1.b == c2.b)
do
diff --git a/firmware/drivers/lcd-2bit-horz.c b/firmware/drivers/lcd-2bit-horz.c
index b2114ba830..abe7443e01 100644
--- a/firmware/drivers/lcd-2bit-horz.c
+++ b/firmware/drivers/lcd-2bit-horz.c
@@ -64,6 +64,7 @@ static struct viewport default_vp =
.y = 0,
.width = LCD_WIDTH,
.height = LCD_HEIGHT,
+ .flags = 0,
.font = FONT_SYSFIXED,
.drawmode = DRMODE_SOLID,
.buffer = NULL,
@@ -80,7 +81,7 @@ static void *lcd_frameaddress_default(int x, int y)
/* the default expects a buffer the same size as the screen */
struct frame_buffer_t *fb = lcd_current_viewport->buffer;
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
size_t element = (x * LCD_NATIVE_STRIDE(fb->stride)) + y;
#else
size_t element = (y * LCD_NATIVE_STRIDE(fb->stride)) + x;
@@ -88,6 +89,8 @@ static void *lcd_frameaddress_default(int x, int y)
return fb->fb_ptr + element;/*(element % fb->elems);*/
}
+#include "lcd-bitmap-common.c"
+
/* LCD init */
void lcd_init(void)
{
@@ -102,16 +105,6 @@ void lcd_init(void)
/*** parameter handling ***/
-void lcd_set_drawmode(int mode)
-{
- lcd_current_viewport->drawmode = mode & (DRMODE_SOLID|DRMODE_INVERSEVID);
-}
-
-int lcd_get_drawmode(void)
-{
- return lcd_current_viewport->drawmode;
-}
-
void lcd_set_foreground(unsigned brightness)
{
lcd_current_viewport->fg_pattern = brightness;
@@ -134,38 +127,6 @@ unsigned lcd_get_background(void)
return lcd_current_viewport->bg_pattern;
}
-void lcd_set_drawinfo(int mode, unsigned fg_brightness, unsigned bg_brightness)
-{
- lcd_set_drawmode(mode);
- lcd_set_foreground(fg_brightness);
- lcd_set_background(bg_brightness);
-}
-
-int lcd_getwidth(void)
-{
- return lcd_current_viewport->width;
-}
-
-int lcd_getheight(void)
-{
- return lcd_current_viewport->height;
-}
-
-void lcd_setfont(int newfont)
-{
- lcd_current_viewport->font = newfont;
-}
-
-int lcd_getfont(void)
-{
- return lcd_current_viewport->font;
-}
-
-int lcd_getstringsize(const unsigned char *str, int *w, int *h)
-{
- return font_getstringsize(str, w, h, lcd_current_viewport->font);
-}
-
/*** low-level drawing functions ***/
static void setpixel(int x, int y)
@@ -384,185 +345,22 @@ void lcd_clear_display(void)
memset(FBADDR(0,0), bg_pattern, FRAMEBUFFER_SIZE);
}
- lcd_scroll_info.lines = 0;
-}
-
-/* Clear the current viewport */
-void lcd_clear_viewport(void)
-{
- int lastmode;
-
- if (lcd_current_viewport == &default_vp &&
- default_vp.buffer == &lcd_framebuffer_default)
- {
- lcd_clear_display();
- }
- else
- {
- lastmode = lcd_current_viewport->drawmode;
-
- /* Invert the INVERSEVID bit and set basic mode to SOLID */
- lcd_current_viewport->drawmode = (~lastmode & DRMODE_INVERSEVID) |
- DRMODE_SOLID;
-
- lcd_fillrect(0, 0, lcd_current_viewport->width, lcd_current_viewport->height);
-
- lcd_current_viewport->drawmode = lastmode;
-
- lcd_scroll_stop_viewport(lcd_current_viewport);
- }
- lcd_current_viewport->flags &= ~(VP_FLAG_VP_SET_CLEAN);
-}
-
-/* Set a single pixel */
-void lcd_drawpixel(int x, int y)
-{
- if ( ((unsigned)x < (unsigned)lcd_current_viewport->width)
- && ((unsigned)y < (unsigned)lcd_current_viewport->height)
-#if defined(HAVE_VIEWPORT_CLIP)
- && ((unsigned)x < (unsigned)LCD_WIDTH)
- && ((unsigned)y < (unsigned)LCD_HEIGHT)
-#endif
- )
- lcd_pixelfuncs[lcd_current_viewport->drawmode](lcd_current_viewport->x + x, lcd_current_viewport->y + y);
-}
-
-/* Draw a line */
-void lcd_drawline(int x1, int y1, int x2, int y2)
-{
- int numpixels;
- int i;
- int deltax, deltay;
- int d, dinc1, dinc2;
- int x, xinc1, xinc2;
- int y, yinc1, yinc2;
- lcd_pixelfunc_type *pfunc = lcd_pixelfuncs[lcd_current_viewport->drawmode];
-
- deltay = abs(y2 - y1);
- if (deltay == 0)
- {
- /* DEBUGF("lcd_drawline() called for horizontal line - optimisation.\n"); */
- lcd_hline(x1, x2, y1);
- return;
- }
- deltax = abs(x2 - x1);
- if (deltax == 0)
- {
- /* DEBUGF("lcd_drawline() called for vertical line - optimisation.\n"); */
- lcd_vline(x1, y1, y2);
- return;
- }
- xinc2 = 1;
- yinc2 = 1;
-
- if (deltax >= deltay)
- {
- numpixels = deltax;
- d = 2 * deltay - deltax;
- dinc1 = deltay * 2;
- dinc2 = (deltay - deltax) * 2;
- xinc1 = 1;
- yinc1 = 0;
- }
- else
- {
- numpixels = deltay;
- d = 2 * deltax - deltay;
- dinc1 = deltax * 2;
- dinc2 = (deltax - deltay) * 2;
- xinc1 = 0;
- yinc1 = 1;
- }
- numpixels++; /* include endpoints */
-
- if (x1 > x2)
- {
- xinc1 = -xinc1;
- xinc2 = -xinc2;
- }
-
- if (y1 > y2)
- {
- yinc1 = -yinc1;
- yinc2 = -yinc2;
- }
-
- x = x1;
- y = y1;
-
- for (i = 0; i < numpixels; i++)
- {
- if ( ((unsigned)x < (unsigned)lcd_current_viewport->width)
- && ((unsigned)y < (unsigned)lcd_current_viewport->height)
-#if defined(HAVE_VIEWPORT_CLIP)
- && ((unsigned)x < (unsigned)LCD_WIDTH)
- && ((unsigned)y < (unsigned)LCD_HEIGHT)
-#endif
- )
- pfunc(lcd_current_viewport->x + x, lcd_current_viewport->y + y);
-
- if (d < 0)
- {
- d += dinc1;
- x += xinc1;
- y += yinc1;
- }
- else
- {
- d += dinc2;
- x += xinc2;
- y += yinc2;
- }
- }
+ lcd_scroll_stop();
}
/* Draw a horizontal line (optimised) */
void lcd_hline(int x1, int x2, int y)
{
+ struct viewport *vp = lcd_current_viewport;
int nx;
unsigned char *dst;
unsigned mask, mask_right;
lcd_blockfunc_type *bfunc;
- /* direction flip */
- if (x2 < x1)
- {
- nx = x1;
- x1 = x2;
- x2 = nx;
- }
-
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if (((unsigned)y >= (unsigned)lcd_current_viewport->height) || (x1 >= lcd_current_viewport->width)
- || (x2 < 0))
- return;
-
- if (x1 < 0)
- x1 = 0;
- if (x2 >= lcd_current_viewport->width)
- x2 = lcd_current_viewport->width-1;
-
- /* adjust to viewport */
- x1 += lcd_current_viewport->x;
- x2 += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if (((unsigned)y >= (unsigned) LCD_HEIGHT) || (x1 >= LCD_WIDTH)
- || (x2 < 0))
+ if (!clip_viewport_hline(vp, &x1, &x2, &y))
return;
- /* clipping */
- if (x1 < 0)
- x1 = 0;
- if (x2 >= LCD_WIDTH)
- x2 = LCD_WIDTH-1;
-#endif
-
- bfunc = lcd_blockfuncs[lcd_current_viewport->drawmode];
+ bfunc = lcd_blockfuncs[vp->drawmode];
dst = FBADDR(x1>>2,y);
nx = x2 - (x1 & ~3);
mask = 0xFFu >> (2 * (x1 & 3));
@@ -580,53 +378,18 @@ void lcd_hline(int x1, int x2, int y)
/* Draw a vertical line (optimised) */
void lcd_vline(int x, int y1, int y2)
{
- int y;
+ struct viewport *vp = lcd_current_viewport;
unsigned char *dst, *dst_end;
int stride_dst;
unsigned mask;
lcd_blockfunc_type *bfunc;
- /* direction flip */
- if (y2 < y1)
- {
- y = y1;
- y1 = y2;
- y2 = y;
- }
-
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if (((unsigned)x >= (unsigned)lcd_current_viewport->width) || (y1 >= lcd_current_viewport->height)
- || (y2 < 0))
- return;
-
- if (y1 < 0)
- y1 = 0;
- if (y2 >= lcd_current_viewport->height)
- y2 = lcd_current_viewport->height-1;
-
- /* adjust for viewport */
- y1 += lcd_current_viewport->y;
- y2 += lcd_current_viewport->y;
- x += lcd_current_viewport->x;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if (( (unsigned) x >= (unsigned)LCD_WIDTH) || (y1 >= LCD_HEIGHT)
- || (y2 < 0))
+ if (!clip_viewport_vline(vp, &x, &y1, &y2))
return;
- /* clipping */
- if (y1 < 0)
- y1 = 0;
- if (y2 >= LCD_HEIGHT)
- y2 = LCD_HEIGHT-1;
-#endif
-
- bfunc = lcd_blockfuncs[lcd_current_viewport->drawmode];
+ bfunc = lcd_blockfuncs[vp->drawmode];
dst = FBADDR(x>>2,y1);
- stride_dst = LCD_FBSTRIDE(lcd_current_viewport->buffer->stride, 0);
+ stride_dst = LCD_FBSTRIDE(vp->buffer->stride, 0);
mask = pixmask[x & 3];
dst_end = dst + (y2 - y1) * stride_dst;
@@ -638,82 +401,22 @@ void lcd_vline(int x, int y1, int y2)
while (dst <= dst_end);
}
-/* Draw a rectangular box */
-void lcd_drawrect(int x, int y, int width, int height)
-{
- if ((width <= 0) || (height <= 0))
- return;
-
- int x2 = x + width - 1;
- int y2 = y + height - 1;
-
- lcd_vline(x, y, y2);
- lcd_vline(x2, y, y2);
- lcd_hline(x, x2, y);
- lcd_hline(x, x2, y2);
-}
-
/* Fill a rectangular area */
void lcd_fillrect(int x, int y, int width, int height)
{
+ struct viewport *vp = lcd_current_viewport;
int nx;
unsigned char *dst, *dst_end;
int stride_dst;
unsigned mask, mask_right;
lcd_blockfunc_type *bfunc;
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= lcd_current_viewport->width) || (y >= lcd_current_viewport->height)
- || (x + width <= 0) || (y + height <= 0))
- return;
-
- if (x < 0)
- {
- width += x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- y = 0;
- }
- if (x + width > lcd_current_viewport->width)
- width = lcd_current_viewport->width - x;
- if (y + height > lcd_current_viewport->height)
- height = lcd_current_viewport->height - y;
-
- /* adjust for viewport */
- x += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, NULL, NULL))
return;
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
-
- bfunc = lcd_blockfuncs[lcd_current_viewport->drawmode];
+ bfunc = lcd_blockfuncs[vp->drawmode];
dst = FBADDR(x>>2,y);
- stride_dst = LCD_FBSTRIDE(lcd_current_viewport->buffer->stride, 0);
+ stride_dst = LCD_FBSTRIDE(vp->buffer->stride, 0);
nx = width - 1 + (x & 3);
mask = 0xFFu >> (2 * (x & 3));
mask_right = 0xFFu << (2 * (~nx & 3));
@@ -758,71 +461,23 @@ void ICODE_ATTR lcd_mono_bitmap_part(const unsigned char *src, int src_x,
int src_y, int stride, int x, int y,
int width, int height)
{
+ struct viewport *vp = lcd_current_viewport;
const unsigned char *src_end;
fb_data *dst, *dst_end;
int stride_dst;
unsigned dmask = 0x100; /* bit 8 == sentinel */
unsigned dst_mask;
- int drmode = lcd_current_viewport->drawmode;
+ int drmode = vp->drawmode;
- /******************** Image in viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= lcd_current_viewport->width) ||
- (y >= lcd_current_viewport->height) || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, &src_x, &src_y))
return;
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > lcd_current_viewport->width)
- width = lcd_current_viewport->width - x;
- if (y + height > lcd_current_viewport->height)
- height = lcd_current_viewport->height - y;
-
- x += lcd_current_viewport->x; /* adjust for viewport */
- y += lcd_current_viewport->y; /* adjust for viewport */
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
- return;
-
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
-
src += stride * (src_y >> 3) + src_x; /* move starting point */
src_y &= 7;
src_end = src + width;
dst = FBADDR(x >> 2,y);
- stride_dst = LCD_FBSTRIDE(lcd_current_viewport->buffer->stride, 0);
+ stride_dst = LCD_FBSTRIDE(vp->buffer->stride, 0);
dst_end = dst + height * stride_dst;
dst_mask = pixmask[x & 3];
@@ -974,71 +629,22 @@ void ICODE_ATTR lcd_bitmap_part(const unsigned char *src, int src_x,
int src_y, int stride, int x, int y,
int width, int height)
{
+ struct viewport *vp = lcd_current_viewport;
int shift, nx;
unsigned char *dst, *dst_end;
int stride_dst;
unsigned mask, mask_right;
- /******************** Image in viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= lcd_current_viewport->width) ||
- (y >= lcd_current_viewport->height) || (x + width <= 0) || (y + height <= 0))
- return;
-
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > lcd_current_viewport->width)
- width = lcd_current_viewport->width - x;
- if (y + height > lcd_current_viewport->height)
- height = lcd_current_viewport->height - y;
-
- /* adjust for viewport */
- x += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, &src_x, &src_y))
return;
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
-
stride = LCD_FBSTRIDE(stride, 0); /* convert to no. of bytes */
src += stride * src_y + (src_x >> 2); /* move starting point */
src_x &= 3;
x -= src_x;
dst = FBADDR(x>>2,y);
- stride_dst = LCD_FBSTRIDE(lcd_current_viewport->buffer->stride, 0);
+ stride_dst = LCD_FBSTRIDE(vp->buffer->stride, 0);
shift = x & 3;
nx = width - 1 + shift + src_x;
@@ -1082,5 +688,3 @@ void lcd_bitmap(const unsigned char *src, int x, int y, int width, int height)
{
lcd_bitmap_part(src, 0, 0, width, x, y, width, height);
}
-
-#include "lcd-bitmap-common.c"
diff --git a/firmware/drivers/lcd-2bit-vert.c b/firmware/drivers/lcd-2bit-vert.c
index 6a476495d0..6ea367e695 100644
--- a/firmware/drivers/lcd-2bit-vert.c
+++ b/firmware/drivers/lcd-2bit-vert.c
@@ -66,6 +66,7 @@ static struct viewport default_vp =
.y = 0,
.width = LCD_WIDTH,
.height = LCD_HEIGHT,
+ .flags = 0,
.font = FONT_SYSFIXED,
.drawmode = DRMODE_SOLID,
.buffer = NULL,
@@ -82,7 +83,7 @@ static void *lcd_frameaddress_default(int x, int y)
/* the default expects a buffer the same size as the screen */
struct frame_buffer_t *fb = lcd_current_viewport->buffer;
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
size_t element = (x * LCD_NATIVE_STRIDE(fb->stride)) + y;
#else
size_t element = (y * LCD_NATIVE_STRIDE(fb->stride)) + x;
@@ -90,6 +91,8 @@ static void *lcd_frameaddress_default(int x, int y)
return fb->fb_ptr + element; /*(element % fb->elems);*/
}
+#include "lcd-bitmap-common.c"
+
/* LCD init */
void lcd_init(void)
{
@@ -104,16 +107,6 @@ void lcd_init(void)
/*** parameter handling ***/
-void lcd_set_drawmode(int mode)
-{
- lcd_current_viewport->drawmode = mode & (DRMODE_SOLID|DRMODE_INVERSEVID);
-}
-
-int lcd_get_drawmode(void)
-{
- return lcd_current_viewport->drawmode;
-}
-
void lcd_set_foreground(unsigned brightness)
{
lcd_current_viewport->fg_pattern = brightness;
@@ -136,38 +129,6 @@ unsigned lcd_get_background(void)
return lcd_current_viewport->bg_pattern;
}
-void lcd_set_drawinfo(int mode, unsigned fg_brightness, unsigned bg_brightness)
-{
- lcd_set_drawmode(mode);
- lcd_set_foreground(fg_brightness);
- lcd_set_background(bg_brightness);
-}
-
-int lcd_getwidth(void)
-{
- return lcd_current_viewport->width;
-}
-
-int lcd_getheight(void)
-{
- return lcd_current_viewport->height;
-}
-
-void lcd_setfont(int newfont)
-{
- lcd_current_viewport->font = newfont;
-}
-
-int lcd_getfont(void)
-{
- return lcd_current_viewport->font;
-}
-
-int lcd_getstringsize(const unsigned char *str, int *w, int *h)
-{
- return font_getstringsize(str, w, h, lcd_current_viewport->font);
-}
-
/*** low-level drawing functions ***/
static void setpixel(int x, int y)
@@ -386,188 +347,24 @@ void lcd_clear_display(void)
memset(FBADDR(0,0), bg_pattern, FRAMEBUFFER_SIZE);
}
- lcd_scroll_info.lines = 0;
-}
-
-/* Clear the current viewport */
-void lcd_clear_viewport(void)
-{
- int lastmode;
-
- if (lcd_current_viewport == &default_vp &&
- default_vp.buffer == &lcd_framebuffer_default)
- {
- lcd_clear_display();
- }
- else
- {
- lastmode = lcd_current_viewport->drawmode;
-
- /* Invert the INVERSEVID bit and set basic mode to SOLID */
- lcd_current_viewport->drawmode = (~lastmode & DRMODE_INVERSEVID) |
- DRMODE_SOLID;
-
- lcd_fillrect(0, 0, lcd_current_viewport->width, lcd_current_viewport->height);
-
- lcd_current_viewport->drawmode = lastmode;
-
- lcd_scroll_stop_viewport(lcd_current_viewport);
- }
- lcd_current_viewport->flags &= ~(VP_FLAG_VP_SET_CLEAN);
-}
-
-/* Set a single pixel */
-void lcd_drawpixel(int x, int y)
-{
- if ( ((unsigned)x < (unsigned)lcd_current_viewport->width)
- && ((unsigned)y < (unsigned)lcd_current_viewport->height)
-#if defined(HAVE_VIEWPORT_CLIP)
- && ((unsigned)x < (unsigned)LCD_WIDTH)
- && ((unsigned)y < (unsigned)LCD_HEIGHT)
-#endif
- )
- lcd_pixelfuncs[lcd_current_viewport->drawmode](lcd_current_viewport->x + x, lcd_current_viewport->y + y);
-}
-
-/* Draw a line */
-void lcd_drawline(int x1, int y1, int x2, int y2)
-{
- int numpixels;
- int i;
- int deltax, deltay;
- int d, dinc1, dinc2;
- int x, xinc1, xinc2;
- int y, yinc1, yinc2;
- lcd_pixelfunc_type *pfunc = lcd_pixelfuncs[lcd_current_viewport->drawmode];
-
- deltax = abs(x2 - x1);
- if (deltax == 0)
- {
- /* DEBUGF("lcd_drawline() called for vertical line - optimisation.\n"); */
- lcd_vline(x1, y1, y2);
- return;
- }
- deltay = abs(y2 - y1);
- if (deltay == 0)
- {
- /* DEBUGF("lcd_drawline() called for horizontal line - optimisation.\n"); */
- lcd_hline(x1, x2, y1);
- return;
- }
- xinc2 = 1;
- yinc2 = 1;
-
- if (deltax >= deltay)
- {
- numpixels = deltax;
- d = 2 * deltay - deltax;
- dinc1 = deltay * 2;
- dinc2 = (deltay - deltax) * 2;
- xinc1 = 1;
- yinc1 = 0;
- }
- else
- {
- numpixels = deltay;
- d = 2 * deltax - deltay;
- dinc1 = deltax * 2;
- dinc2 = (deltax - deltay) * 2;
- xinc1 = 0;
- yinc1 = 1;
- }
- numpixels++; /* include endpoints */
-
- if (x1 > x2)
- {
- xinc1 = -xinc1;
- xinc2 = -xinc2;
- }
-
- if (y1 > y2)
- {
- yinc1 = -yinc1;
- yinc2 = -yinc2;
- }
-
- x = x1;
- y = y1;
-
- for (i = 0; i < numpixels; i++)
- {
- if ( ((unsigned)x < (unsigned)lcd_current_viewport->width)
- && ((unsigned)y < (unsigned)lcd_current_viewport->height)
-#if defined(HAVE_VIEWPORT_CLIP)
- && ((unsigned)x < (unsigned)LCD_WIDTH)
- && ((unsigned)y < (unsigned)LCD_HEIGHT)
-#endif
- )
- pfunc(lcd_current_viewport->x + x, lcd_current_viewport->y + y);
-
- if (d < 0)
- {
- d += dinc1;
- x += xinc1;
- y += yinc1;
- }
- else
- {
- d += dinc2;
- x += xinc2;
- y += yinc2;
- }
- }
+ lcd_scroll_stop();
}
/* Draw a horizontal line (optimised) */
void lcd_hline(int x1, int x2, int y)
{
- int x;
+ struct viewport *vp = lcd_current_viewport;
int width;
fb_data *dst, *dst_end;
unsigned mask;
lcd_blockfunc_type *bfunc;
- /* direction flip */
- if (x2 < x1)
- {
- x = x1;
- x1 = x2;
- x2 = x;
- }
-
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if (((unsigned)y >= (unsigned)lcd_current_viewport->height) || (x1 >= lcd_current_viewport->width)
- || (x2 < 0))
- return;
-
- if (x1 < 0)
- x1 = 0;
- if (x2 >= lcd_current_viewport->width)
- x2 = lcd_current_viewport->width-1;
-
- /* adjust x1 and y to viewport */
- x1 += lcd_current_viewport->x;
- x2 += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if (((unsigned)y >= (unsigned) LCD_HEIGHT) || (x1 >= LCD_WIDTH)
- || (x2 < 0))
+ if (!clip_viewport_hline(vp, &x1, &x2, &y))
return;
- /* clipping */
- if (x1 < 0)
- x1 = 0;
- if (x2 >= LCD_WIDTH)
- x2 = LCD_WIDTH-1;
-#endif
-
width = x2 - x1 + 1;
- bfunc = lcd_blockfuncs[lcd_current_viewport->drawmode];
+ bfunc = lcd_blockfuncs[vp->drawmode];
dst = FBADDR(x1,y>>2);
mask = pixmask[y & 3];
@@ -580,53 +377,19 @@ void lcd_hline(int x1, int x2, int y)
/* Draw a vertical line (optimised) */
void lcd_vline(int x, int y1, int y2)
{
+ struct viewport *vp = lcd_current_viewport;
int ny;
fb_data *dst;
int stride_dst;
unsigned mask, mask_bottom;
lcd_blockfunc_type *bfunc;
- /* direction flip */
- if (y2 < y1)
- {
- ny = y1;
- y1 = y2;
- y2 = ny;
- }
-
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if (((unsigned)x >= (unsigned)lcd_current_viewport->width) || (y1 >= lcd_current_viewport->height)
- || (y2 < 0))
- return;
-
- if (y1 < 0)
- y1 = 0;
- if (y2 >= lcd_current_viewport->height)
- y2 = lcd_current_viewport->height-1;
-
- /* adjust for viewport */
- y1 += lcd_current_viewport->y;
- y2 += lcd_current_viewport->y;
- x += lcd_current_viewport->x;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if (( (unsigned) x >= (unsigned)LCD_WIDTH) || (y1 >= LCD_HEIGHT)
- || (y2 < 0))
+ if (!clip_viewport_vline(vp, &x, &y1, &y2))
return;
- /* clipping */
- if (y1 < 0)
- y1 = 0;
- if (y2 >= LCD_HEIGHT)
- y2 = LCD_HEIGHT-1;
-#endif
-
- bfunc = lcd_blockfuncs[lcd_current_viewport->drawmode];
+ bfunc = lcd_blockfuncs[vp->drawmode];
dst = FBADDR(x,y1>>2);
- stride_dst = lcd_current_viewport->buffer->stride;
+ stride_dst = vp->buffer->stride;
ny = y2 - (y1 & ~3);
mask = 0xFFu << (2 * (y1 & 3));
mask_bottom = 0xFFu >> (2 * (~ny & 3));
@@ -641,24 +404,10 @@ void lcd_vline(int x, int y1, int y2)
bfunc(dst, mask, 0xFFu);
}
-/* Draw a rectangular box */
-void lcd_drawrect(int x, int y, int width, int height)
-{
- if ((width <= 0) || (height <= 0))
- return;
-
- int x2 = x + width - 1;
- int y2 = y + height - 1;
-
- lcd_vline(x, y, y2);
- lcd_vline(x2, y, y2);
- lcd_hline(x, x2, y);
- lcd_hline(x, x2, y2);
-}
-
/* Fill a rectangular area */
void lcd_fillrect(int x, int y, int width, int height)
{
+ struct viewport *vp = lcd_current_viewport;
int ny;
fb_data *dst, *dst_end;
int stride_dst;
@@ -667,58 +416,12 @@ void lcd_fillrect(int x, int y, int width, int height)
lcd_blockfunc_type *bfunc;
bool fillopt = false;
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= lcd_current_viewport->width)
- || (y >= lcd_current_viewport->height) || (x + width <= 0) || (y + height <= 0))
- return;
-
- if (x < 0)
- {
- width += x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- y = 0;
- }
- if (x + width > lcd_current_viewport->width)
- width = lcd_current_viewport->width - x;
- if (y + height > lcd_current_viewport->height)
- height = lcd_current_viewport->height - y;
-
- /* adjust for viewport */
- x += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, NULL, NULL))
return;
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- x = 0;
- }
- if (y < 0)
+ if (vp->drawmode & DRMODE_INVERSEVID)
{
- height += y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
-
- if (lcd_current_viewport->drawmode & DRMODE_INVERSEVID)
- {
- if ((lcd_current_viewport->drawmode & DRMODE_BG) && !lcd_backdrop)
+ if ((vp->drawmode & DRMODE_BG) && !lcd_backdrop)
{
fillopt = true;
bits = bg_pattern;
@@ -726,15 +429,15 @@ void lcd_fillrect(int x, int y, int width, int height)
}
else
{
- if (lcd_current_viewport->drawmode & DRMODE_FG)
+ if (vp->drawmode & DRMODE_FG)
{
fillopt = true;
bits = fg_pattern;
}
}
- bfunc = lcd_blockfuncs[lcd_current_viewport->drawmode];
+ bfunc = lcd_blockfuncs[vp->drawmode];
dst = FBADDR(x,y>>2);
- stride_dst = lcd_current_viewport->buffer->stride;
+ stride_dst = vp->buffer->stride;
ny = height - 1 + (y & 3);
mask = 0xFFu << (2 * (y & 3));
mask_bottom = 0xFFu >> (2 * (~ny & 3));
@@ -785,77 +488,28 @@ void ICODE_ATTR lcd_mono_bitmap_part(const unsigned char *src, int src_x,
int src_y, int stride, int x, int y,
int width, int height)
{
+ struct viewport *vp = lcd_current_viewport;
int shift, ny;
fb_data *dst, *dst_end;
int stride_dst;
unsigned mask, mask_bottom;
lcd_blockfunc_type *bfunc;
- /******************** Image in viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= lcd_current_viewport->width) ||
- (y >= lcd_current_viewport->height) || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, &src_x, &src_y))
return;
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > lcd_current_viewport->width)
- width = lcd_current_viewport->width - x;
- if (y + height > lcd_current_viewport->height)
- height = lcd_current_viewport->height - y;
-
- /* adjust for viewport */
- x += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
- return;
-
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
-
src += stride * (src_y >> 3) + src_x; /* move starting point */
src_y &= 7;
y -= src_y;
dst = FBADDR(x,y>>2);
- stride_dst = lcd_current_viewport->buffer->stride;
+ stride_dst = vp->buffer->stride;
shift = y & 3;
ny = height - 1 + shift + src_y;
mask = 0xFFFFu << (2 * (shift + src_y));
/* Overflowing bits aren't important. */
mask_bottom = 0xFFFFu >> (2 * (~ny & 7));
- bfunc = lcd_blockfuncs[lcd_current_viewport->drawmode];
+ bfunc = lcd_blockfuncs[vp->drawmode];
if (shift == 0)
{
@@ -985,68 +639,20 @@ void ICODE_ATTR lcd_bitmap_part(const fb_data *src, int src_x, int src_y,
int stride, int x, int y, int width,
int height)
{
+ struct viewport *vp = lcd_current_viewport;
int shift, ny;
fb_data *dst, *dst_end;
int stride_dst;
unsigned mask, mask_bottom;
- /******************** Image in viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= lcd_current_viewport->width)
- || (y >= lcd_current_viewport->height) || (x + width <= 0) || (y + height <= 0))
- return;
-
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > lcd_current_viewport->width)
- width = lcd_current_viewport->width - x;
- if (y + height > lcd_current_viewport->height)
- height = lcd_current_viewport->height - y;
-
- /* adjust for viewport */
- x += lcd_current_viewport->x;
- y += lcd_current_viewport->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
- || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, &src_x, &src_y))
return;
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height > LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-#endif
src += stride * (src_y >> 2) + src_x; /* move starting point */
src_y &= 3;
y -= src_y;
dst = FBADDR(x,y>>2);
- stride_dst = lcd_current_viewport->buffer->stride;
+ stride_dst = vp->buffer->stride;
shift = y & 3;
ny = height - 1 + shift + src_y;
@@ -1124,5 +730,3 @@ void lcd_bitmap(const fb_data *src, int x, int y, int width, int height)
{
lcd_bitmap_part(src, 0, 0, width, x, y, width, height);
}
-
-#include "lcd-bitmap-common.c"
diff --git a/firmware/drivers/lcd-2bit-vi.c b/firmware/drivers/lcd-2bit-vi.c
index c09bbce2e0..5c615c4c9e 100644
--- a/firmware/drivers/lcd-2bit-vi.c
+++ b/firmware/drivers/lcd-2bit-vi.c
@@ -78,6 +78,7 @@ static struct viewport default_vp =
.y = 0,
.width = LCDM(WIDTH),
.height = LCDM(HEIGHT),
+ .flags = 0,
.font = FONT_SYSFIXED,
.drawmode = DRMODE_SOLID,
.buffer = NULL,
@@ -94,7 +95,7 @@ static void *LCDFN(frameaddress_default)(int x, int y)
{
/* the default expects a buffer the same size as the screen */
struct frame_buffer_t *fb = CURRENT_VP->buffer;
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if defined(MAIN_LCD) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
size_t element = (x * LCDM(NATIVE_STRIDE)(fb->stride)) + y;
#else
size_t element = (y * LCDM(NATIVE_STRIDE)(fb->stride)) + x;
@@ -102,6 +103,8 @@ static void *LCDFN(frameaddress_default)(int x, int y)
return fb->FBFN(ptr) + element;/*(element % fb->elems);*/
}
+#include "lcd-bitmap-common.c"
+
/* LCD init */
void LCDFN(init)(void)
{
@@ -133,16 +136,6 @@ unsigned lcd_remote_color_to_native(unsigned color)
}
#endif
-void LCDFN(set_drawmode)(int mode)
-{
- CURRENT_VP->drawmode = mode & (DRMODE_SOLID|DRMODE_INVERSEVID);
-}
-
-int LCDFN(get_drawmode)(void)
-{
- return CURRENT_VP->drawmode;
-}
-
void LCDFN(set_foreground)(unsigned brightness)
{
CURRENT_VP->fg_pattern = brightness;
@@ -165,38 +158,6 @@ unsigned LCDFN(get_background)(void)
return CURRENT_VP->bg_pattern;
}
-void LCDFN(set_drawinfo)(int mode, unsigned fg_brightness,
- unsigned bg_brightness)
-{
- LCDFN(set_drawmode)(mode);
- LCDFN(set_foreground)(fg_brightness);
- LCDFN(set_background)(bg_brightness);
-}
-
-int LCDFN(getwidth)(void)
-{
- return CURRENT_VP->width;
-}
-
-int LCDFN(getheight)(void)
-{
- return CURRENT_VP->height;
-}
-void LCDFN(setfont)(int newfont)
-{
- CURRENT_VP->font = newfont;
-}
-
-int LCDFN(getfont)(void)
-{
- return CURRENT_VP->font;
-}
-
-int LCDFN(getstringsize)(const unsigned char *str, int *w, int *h)
-{
- return font_getstringsize(str, w, h, CURRENT_VP->font);
-}
-
/*** low-level drawing functions ***/
static void setpixel(int x, int y)
@@ -419,188 +380,24 @@ void LCDFN(clear_display)(void)
FBSIZE);
}
- LCDFN(scroll_info).lines = 0;
-}
-
-/* Clear the current viewport */
-void LCDFN(clear_viewport)(void)
-{
- int lastmode;
-
- if (CURRENT_VP == &default_vp &&
- default_vp.buffer == &LCDFN(framebuffer_default))
- {
- LCDFN(clear_display)();
- }
- else
- {
- lastmode = CURRENT_VP->drawmode;
-
- /* Invert the INVERSEVID bit and set basic mode to SOLID */
- CURRENT_VP->drawmode = (~lastmode & DRMODE_INVERSEVID) |
- DRMODE_SOLID;
-
- LCDFN(fillrect)(0, 0, CURRENT_VP->width, CURRENT_VP->height);
-
- CURRENT_VP->drawmode = lastmode;
-
- LCDFN(scroll_stop_viewport)(CURRENT_VP);
- }
- CURRENT_VP->flags &= ~(VP_FLAG_VP_SET_CLEAN);
-}
-
-/* Set a single pixel */
-void LCDFN(drawpixel)(int x, int y)
-{
- if ( ((unsigned)x < (unsigned)CURRENT_VP->width)
- && ((unsigned)y < (unsigned)CURRENT_VP->height)
-#if defined(HAVE_VIEWPORT_CLIP)
- && ((unsigned)x < (unsigned)LCDM(WIDTH))
- && ((unsigned)y < (unsigned)LCDM(HEIGHT))
-#endif
- )
- LCDFN(pixelfuncs)[CURRENT_VP->drawmode](CURRENT_VP->x+x, CURRENT_VP->y+y);
-}
-
-/* Draw a line */
-void LCDFN(drawline)(int x1, int y1, int x2, int y2)
-{
- int numpixels;
- int i;
- int deltax, deltay;
- int d, dinc1, dinc2;
- int x, xinc1, xinc2;
- int y, yinc1, yinc2;
- LCDFN(pixelfunc_type) *pfunc = LCDFN(pixelfuncs)[CURRENT_VP->drawmode];
-
- deltax = abs(x2 - x1);
- if (deltax == 0)
- {
- /* DEBUGF(LCDNAME "drawline() called for vertical line - optimisation.\n"); */
- LCDFN(vline)(x1, y1, y2);
- return;
- }
- deltay = abs(y2 - y1);
- if (deltay == 0)
- {
- /* DEBUGF(LCDNAME "drawline() called for horizontal line - optimisation.\n"); */
- LCDFN(hline)(x1, x2, y1);
- return;
- }
- xinc2 = 1;
- yinc2 = 1;
-
- if (deltax >= deltay)
- {
- numpixels = deltax;
- d = 2 * deltay - deltax;
- dinc1 = deltay * 2;
- dinc2 = (deltay - deltax) * 2;
- xinc1 = 1;
- yinc1 = 0;
- }
- else
- {
- numpixels = deltay;
- d = 2 * deltax - deltay;
- dinc1 = deltax * 2;
- dinc2 = (deltax - deltay) * 2;
- xinc1 = 0;
- yinc1 = 1;
- }
- numpixels++; /* include endpoints */
-
- if (x1 > x2)
- {
- xinc1 = -xinc1;
- xinc2 = -xinc2;
- }
-
- if (y1 > y2)
- {
- yinc1 = -yinc1;
- yinc2 = -yinc2;
- }
-
- x = x1;
- y = y1;
-
- for (i = 0; i < numpixels; i++)
- {
- if ( ((unsigned)x < (unsigned)CURRENT_VP->width)
- && ((unsigned)y < (unsigned)CURRENT_VP->height)
-#if defined(HAVE_VIEWPORT_CLIP)
- && ((unsigned)x < (unsigned)LCDM(WIDTH))
- && ((unsigned)y < (unsigned)LCDM(HEIGHT))
-#endif
- )
- pfunc(CURRENT_VP->x + x, CURRENT_VP->y + y);
-
- if (d < 0)
- {
- d += dinc1;
- x += xinc1;
- y += yinc1;
- }
- else
- {
- d += dinc2;
- x += xinc2;
- y += yinc2;
- }
- }
+ LCDFN(scroll_stop)();
}
/* Draw a horizontal line (optimised) */
void LCDFN(hline)(int x1, int x2, int y)
{
- int x;
+ struct viewport *vp = CURRENT_VP;
int width;
FBFN(data) *dst, *dst_end;
unsigned mask;
LCDFN(blockfunc_type) *bfunc;
- /* direction flip */
- if (x2 < x1)
- {
- x = x1;
- x1 = x2;
- x2 = x;
- }
-
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if (((unsigned)y >= (unsigned)CURRENT_VP->height) || (x1 >= CURRENT_VP->width)
- || (x2 < 0))
- return;
-
- if (x1 < 0)
- x1 = 0;
- if (x2 >= CURRENT_VP->width)
- x2 = CURRENT_VP->width-1;
-
- /* adjust x1 and y to viewport */
- x1 += CURRENT_VP->x;
- x2 += CURRENT_VP->x;
- y += CURRENT_VP->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if (((unsigned)y >= (unsigned) LCDM(HEIGHT)) || (x1 >= LCDM(WIDTH))
- || (x2 < 0))
+ if (!clip_viewport_hline(vp, &x1, &x2, &y))
return;
- /* clipping */
- if (x1 < 0)
- x1 = 0;
- if (x2 >= LCDM(WIDTH))
- x2 = LCDM(WIDTH)-1;
-#endif
-
width = x2 - x1 + 1;
- bfunc = LCDFN(blockfuncs)[CURRENT_VP->drawmode];
+ bfunc = LCDFN(blockfuncs)[vp->drawmode];
dst = LCDFB(x1,y>>3);
mask = 0x0101 << (y & 7);
@@ -613,53 +410,19 @@ void LCDFN(hline)(int x1, int x2, int y)
/* Draw a vertical line (optimised) */
void LCDFN(vline)(int x, int y1, int y2)
{
+ struct viewport *vp = CURRENT_VP;
int ny;
FBFN(data) *dst;
int stride_dst;
unsigned mask, mask_bottom;
LCDFN(blockfunc_type) *bfunc;
- /* direction flip */
- if (y2 < y1)
- {
- ny = y1;
- y1 = y2;
- y2 = ny;
- }
-
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if (((unsigned)x >= (unsigned)CURRENT_VP->width) || (y1 >= CURRENT_VP->height)
- || (y2 < 0))
+ if (!clip_viewport_vline(vp, &x, &y1, &y2))
return;
- if (y1 < 0)
- y1 = 0;
- if (y2 >= CURRENT_VP->height)
- y2 = CURRENT_VP->height-1;
-
- /* adjust for viewport */
- y1 += CURRENT_VP->y;
- y2 += CURRENT_VP->y;
- x += CURRENT_VP->x;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if (( (unsigned) x >= (unsigned)LCDM(WIDTH)) || (y1 >= LCDM(HEIGHT))
- || (y2 < 0))
- return;
-
- /* clipping */
- if (y1 < 0)
- y1 = 0;
- if (y2 >= LCDM(HEIGHT))
- y2 = LCDM(HEIGHT)-1;
-#endif
-
- bfunc = LCDFN(blockfuncs)[CURRENT_VP->drawmode];
+ bfunc = LCDFN(blockfuncs)[vp->drawmode];
dst = LCDFB(x,y1>>3);
- stride_dst = CURRENT_VP->buffer->stride;
+ stride_dst = vp->buffer->stride;
ny = y2 - (y1 & ~7);
mask = (0xFFu << (y1 & 7)) & 0xFFu;
mask |= mask << 8;
@@ -676,24 +439,10 @@ void LCDFN(vline)(int x, int y1, int y2)
bfunc(dst, mask, 0xFFFFu);
}
-/* Draw a rectangular box */
-void LCDFN(drawrect)(int x, int y, int width, int height)
-{
- if ((width <= 0) || (height <= 0))
- return;
-
- int x2 = x + width - 1;
- int y2 = y + height - 1;
-
- LCDFN(vline)(x, y, y2);
- LCDFN(vline)(x2, y, y2);
- LCDFN(hline)(x, x2, y);
- LCDFN(hline)(x, x2, y2);
-}
-
/* Fill a rectangular area */
void LCDFN(fillrect)(int x, int y, int width, int height)
{
+ struct viewport *vp = CURRENT_VP;
int ny;
FBFN(data) *dst, *dst_end;
int stride_dst;
@@ -702,59 +451,12 @@ void LCDFN(fillrect)(int x, int y, int width, int height)
LCDFN(blockfunc_type) *bfunc;
bool fillopt = false;
- /******************** In viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= CURRENT_VP->width)
- || (y >= CURRENT_VP->height) || (x + width <= 0) || (y + height <= 0))
- return;
-
- if (x < 0)
- {
- width += x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- y = 0;
- }
- if (x + width > CURRENT_VP->width)
- width = CURRENT_VP->width - x;
- if (y + height > CURRENT_VP->height)
- height = CURRENT_VP->height - y;
-
- /* adjust for viewport */
- x += CURRENT_VP->x;
- y += CURRENT_VP->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCDM(WIDTH)) || (y >= LCDM(HEIGHT))
- || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, NULL, NULL))
return;
- /* clip image in viewport in screen */
- if (x < 0)
+ if (vp->drawmode & DRMODE_INVERSEVID)
{
- width += x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- y = 0;
- }
- if (x + width > LCDM(WIDTH))
- width = LCDM(WIDTH) - x;
- if (y + height > LCDM(HEIGHT))
- height = LCDM(HEIGHT) - y;
-#endif
-
-
- if (CURRENT_VP->drawmode & DRMODE_INVERSEVID)
- {
- if ((CURRENT_VP->drawmode & DRMODE_BG) && !backdrop)
+ if ((vp->drawmode & DRMODE_BG) && !backdrop)
{
fillopt = true;
bits = bg_pattern;
@@ -762,15 +464,15 @@ void LCDFN(fillrect)(int x, int y, int width, int height)
}
else
{
- if (CURRENT_VP->drawmode & DRMODE_FG)
+ if (vp->drawmode & DRMODE_FG)
{
fillopt = true;
bits = fg_pattern;
}
}
- bfunc = LCDFN(blockfuncs)[CURRENT_VP->drawmode];
+ bfunc = LCDFN(blockfuncs)[vp->drawmode];
dst = LCDFB(x,y>>3);
- stride_dst = CURRENT_VP->buffer->stride;
+ stride_dst = vp->buffer->stride;
ny = height - 1 + (y & 7);
mask = (0xFFu << (y & 7)) & 0xFFu;
mask |= mask << 8;
@@ -823,74 +525,25 @@ void ICODE_ATTR LCDFN(mono_bitmap_part)(const unsigned char *src, int src_x,
int src_y, int stride, int x, int y,
int width, int height)
{
+ struct viewport *vp = CURRENT_VP;
int shift, ny;
FBFN(data) *dst, *dst_end;
int stride_dst;
unsigned data, mask, mask_bottom;
LCDFN(blockfunc_type) *bfunc;
- /******************** Image in viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= CURRENT_VP->width) ||
- (y >= CURRENT_VP->height) || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, &src_x, &src_y))
return;
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > CURRENT_VP->width)
- width = CURRENT_VP->width - x;
- if (y + height > CURRENT_VP->height)
- height = CURRENT_VP->height - y;
-
- /* adjust for viewport */
- x += CURRENT_VP->x;
- y += CURRENT_VP->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCDM(WIDTH)) || (y >= LCDM(HEIGHT))
- || (x + width <= 0) || (y + height <= 0))
- return;
-
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > LCDM(WIDTH))
- width = LCDM(WIDTH) - x;
- if (y + height > LCDM(HEIGHT))
- height = LCDM(HEIGHT) - y;
-#endif
-
src += stride * (src_y >> 3) + src_x; /* move starting point */
src_y &= 7;
y -= src_y;
dst = LCDFB(x,y>>3);
- stride_dst = CURRENT_VP->buffer->stride;
+ stride_dst = vp->buffer->stride;
shift = y & 7;
ny = height - 1 + shift + src_y;
- bfunc = LCDFN(blockfuncs)[CURRENT_VP->drawmode];
+ bfunc = LCDFN(blockfuncs)[vp->drawmode];
mask = 0xFFu << (shift + src_y);
/* not byte-doubled here because shift+src_y can be > 7 */
mask_bottom = 0xFFu >> (~ny & 7);
@@ -994,69 +647,20 @@ void ICODE_ATTR LCDFN(bitmap_part)(const FBFN(data) *src, int src_x,
int src_y, int stride, int x, int y,
int width, int height)
{
+ struct viewport *vp = CURRENT_VP;
int shift, ny;
FBFN(data) *dst, *dst_end;
int stride_dst;
unsigned mask, mask_bottom;
- /******************** Image in viewport clipping **********************/
- /* nothing to draw? */
- if ((width <= 0) || (height <= 0) || (x >= CURRENT_VP->width)
- || (y >= CURRENT_VP->height) || (x + width <= 0) || (y + height <= 0))
- return;
-
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > CURRENT_VP->width)
- width = CURRENT_VP->width - x;
- if (y + height > CURRENT_VP->height)
- height = CURRENT_VP->height - y;
-
- /* adjust for viewport */
- x += CURRENT_VP->x;
- y += CURRENT_VP->y;
-
-#if defined(HAVE_VIEWPORT_CLIP)
- /********************* Viewport on screen clipping ********************/
- /* nothing to draw? */
- if ((x >= LCDM(WIDTH)) || (y >= LCDM(HEIGHT))
- || (x + width <= 0) || (y + height <= 0))
+ if (!clip_viewport_rect(vp, &x, &y, &width, &height, &src_x, &src_y))
return;
- /* clip image in viewport in screen */
- if (x < 0)
- {
- width += x;
- src_x -= x;
- x = 0;
- }
- if (y < 0)
- {
- height += y;
- src_y -= y;
- y = 0;
- }
- if (x + width > LCDM(WIDTH))
- width = LCDM(WIDTH) - x;
- if (y + height > LCDM(HEIGHT))
- height = LCDM(HEIGHT) - y;
-#endif
-
src += stride * (src_y >> 3) + src_x; /* move starting point */
src_y &= 7;
y -= src_y;
dst = LCDFB(x,y>>3);
- stride_dst = CURRENT_VP->buffer->stride;
+ stride_dst = vp->buffer->stride;
shift = y & 7;
ny = height - 1 + shift + src_y;
@@ -1148,5 +752,3 @@ void LCDFN(bitmap)(const FBFN(data) *src, int x, int y, int width, int height)
{
LCDFN(bitmap_part)(src, 0, 0, width, x, y, width, height);
}
-
-#include "lcd-bitmap-common.c"
diff --git a/firmware/drivers/lcd-bitmap-common.c b/firmware/drivers/lcd-bitmap-common.c
index 170ad374ea..975c494b5a 100644
--- a/firmware/drivers/lcd-bitmap-common.c
+++ b/firmware/drivers/lcd-bitmap-common.c
@@ -53,6 +53,158 @@
extern void viewport_set_buffer(struct viewport *vp,
struct frame_buffer_t *buffer,
const enum screen_type screen); /* viewport.c */
+
+/*
+ * In-viewport clipping functions:
+ *
+ * These clip a primitive (pixel, line, rect) given in
+ * viewport-relative coordinates to the specified viewport,
+ * and translate it to screen coordinates. They return
+ * false if the resulting primitive would be off-screen.
+ */
+
+static inline bool clip_viewport_pixel(struct viewport *vp, int *x, int *y)
+{
+ if (*x < 0 || *x >= vp->width ||
+ *y < 0 || *y >= vp->height)
+ return false;
+
+ *x += vp->x;
+ *y += vp->y;
+ return true;
+}
+
+static inline bool clip_viewport_hline(struct viewport *vp,
+ int *x1, int *x2, int *y)
+{
+ if (*y < 0 || *y >= vp->height)
+ return false;
+
+ if (*x2 < *x1) {
+ int tmp = *x2;
+ *x2 = *x1;
+ *x1 = tmp;
+ }
+
+ if (*x1 < 0)
+ *x1 = 0;
+ else if (*x1 >= vp->width)
+ return false;
+
+ if (*x2 < 0)
+ return false;
+ else if (*x2 >= vp->width)
+ *x2 = vp->width - 1;
+
+ *x1 += vp->x;
+ *x2 += vp->x;
+ *y += vp->y;
+ return true;
+}
+
+static inline bool clip_viewport_vline(struct viewport *vp,
+ int *x, int *y1, int *y2)
+{
+ if (*x < 0 || *x >= vp->width)
+ return false;
+
+ if (*y2 < *y1) {
+ int tmp = *y2;
+ *y2 = *y1;
+ *y1 = tmp;
+ }
+
+ if (*y1 < 0)
+ *y1 = 0;
+ else if (*y1 >= vp->height)
+ return false;
+
+ if (*y2 < 0)
+ return false;
+ else if (*y2 >= vp->height)
+ *y2 = vp->height - 1;
+
+ *x += vp->x;
+ *y1 += vp->y;
+ *y2 += vp->y;
+ return true;
+}
+
+static inline bool clip_viewport_rect(struct viewport *vp,
+ int *x, int *y, int *width, int *height,
+ int *src_x, int *src_y)
+{
+ if (*x < 0) {
+ *width += *x;
+ if (src_x)
+ *src_x -= *x;
+ *x = 0;
+ }
+
+ if (*y < 0) {
+ *height += *y;
+ if (src_y)
+ *src_y -= *y;
+ *y = 0;
+ }
+
+ if (*x + *width > vp->width)
+ *width = vp->width - *x;
+
+ if (*y + *height > vp->height)
+ *height = vp->height - *y;
+
+ *x += vp->x;
+ *y += vp->y;
+ return *width > 0 && *height > 0;
+}
+
+/*** parameter handling ***/
+
+void LCDFN(set_drawmode)(int mode)
+{
+ LCDFN(current_viewport)->drawmode = mode & (DRMODE_SOLID|DRMODE_INVERSEVID);
+}
+
+int LCDFN(get_drawmode)(void)
+{
+ return LCDFN(current_viewport)->drawmode;
+}
+
+int LCDFN(getwidth)(void)
+{
+ return LCDFN(current_viewport)->width;
+}
+
+int LCDFN(getheight)(void)
+{
+ return LCDFN(current_viewport)->height;
+}
+
+void LCDFN(setfont)(int newfont)
+{
+ LCDFN(current_viewport)->font = newfont;
+}
+
+int LCDFN(getfont)(void)
+{
+ return LCDFN(current_viewport)->font;
+}
+
+int LCDFN(getstringsize)(const unsigned char *str, int *w, int *h)
+{
+ return font_getstringsize(str, w, h, LCDFN(current_viewport)->font);
+}
+
+#if LCDM(DEPTH) > 1
+void LCDFN(set_drawinfo)(int mode, unsigned foreground, unsigned background)
+{
+ LCDFN(set_drawmode)(mode);
+ LCDFN(set_foreground)(foreground);
+ LCDFN(set_background)(background);
+}
+#endif
+
/*
* draws the borders of the current viewport
**/
@@ -69,6 +221,34 @@ void LCDFN(fill_viewport)(void)
LCDFN(fillrect)(0, 0, LCDFN(current_viewport)->width, LCDFN(current_viewport)->height);
}
+#if LCDM(DEPTH) < 8
+/*
+ * clear the current viewport - grayscale displays
+ */
+void LCDFN(clear_viewport)(void)
+{
+ struct viewport *vp = LCDFN(current_viewport);
+ int oldmode;
+
+ if (vp == &default_vp && default_vp.buffer == &LCDFN(framebuffer_default))
+ {
+ LCDFN(clear_display)();
+ }
+ else
+ {
+ oldmode = vp->drawmode;
+ vp->drawmode ^= DRMODE_INVERSEVID;
+ vp->drawmode |= DRMODE_SOLID;
+
+ LCDFN(fillrect)(0, 0, vp->width, vp->height);
+
+ vp->drawmode = oldmode;
+ LCDFN(scroll_stop_viewport)(vp);
+ }
+
+ vp->flags &= ~VP_FLAG_VP_SET_CLEAN;
+}
+#endif
/*** Viewports ***/
/* init_viewport Notes: When a viewport is initialized
@@ -84,6 +264,7 @@ struct viewport* LCDFN(init_viewport)(struct viewport* vp)
{
vp = &default_vp;
vp->buffer = fb_default;
+ return vp;
}
/* use defaults if no buffer is provided */
@@ -132,16 +313,13 @@ struct viewport* LCDFN(set_viewport_ex)(struct viewport* vp, int flags)
* expected.
*/
- if((unsigned) vp->x > (unsigned) LCDM(WIDTH)
- || (unsigned) vp->y > (unsigned) LCDM(HEIGHT)
+ if( vp->x < 0 || vp->y < 0
+ || vp->x > LCDM(WIDTH)
+ || vp->y > LCDM(HEIGHT)
|| vp->x + vp->width > LCDM(WIDTH)
|| vp->y + vp->height > LCDM(HEIGHT))
{
-#if !defined(HAVE_VIEWPORT_CLIP)
DEBUGF("ERROR: "
-#else
- DEBUGF("NOTE: "
-#endif
"set_viewport out of bounds: x: %d y: %d width: %d height:%d\n",
vp->x, vp->y, vp->width, vp->height);
}
@@ -162,33 +340,22 @@ struct viewport* LCDFN(set_viewport)(struct viewport* vp)
return LCDFN(set_viewport_ex)(vp, VP_FLAG_VP_DIRTY);
}
-struct viewport *LCDFN(get_viewport)(bool *is_default)
-{
-#if 0
- *is_default = memcmp(LCDFN(current_viewport),
- &default_vp, sizeof(struct viewport)) == 0;
-#else
- *is_default = LCDFN(current_viewport) == &default_vp;
-#endif
-
- return LCDFN(current_viewport);
-}
-
void LCDFN(update_viewport)(void)
{
struct viewport* vp = LCDFN(current_viewport);
- if (vp->buffer->stride != LCDFN(framebuffer_default.stride))
- {
- LCDFN(update_viewport_rect)(0,0, vp->width, vp->height);
- return;
- }
- LCDFN(update_rect)(vp->x, vp->y, vp->width, vp->height);
+ LCDFN(update_viewport_rect)(0, 0, vp->width, vp->height);
}
void LCDFN(update_viewport_rect)(int x, int y, int width, int height)
{
struct viewport* vp = LCDFN(current_viewport);
-
+ if ((vp->flags & VP_FLAG_OWNER_UPDATE) == VP_FLAG_OWNER_UPDATE)
+ {
+#ifdef LOGF_ENABLE
+ logf("%s ignored - owner update", __func__);
+#endif
+ return;
+ }
/* handle the case of viewport with differing stride from main screen */
if (vp->buffer->stride != LCDFN(framebuffer_default.stride))
{
@@ -511,9 +678,10 @@ static bool LCDFN(puts_scroll_worker)(int x, int y, const unsigned char *string,
x = x * (linebased ? cwidth : 1);
width = vp->width - x;
- if (y >= vp->height || (height + y) > (vp->height))
+ if (y >= vp->height)
return false;
-
+ if ((height + y) > (vp->height))
+ height = vp->height - y;
s = find_scrolling_line(x, y);
restart = !s;
@@ -531,7 +699,7 @@ static bool LCDFN(puts_scroll_worker)(int x, int y, const unsigned char *string,
}
/* copy contents to the line buffer */
- strlcpy(s->linebuffer, string, sizeof(s->linebuffer));
+ strmemccpy(s->linebuffer, string, sizeof(s->linebuffer));
/* scroll bidirectional or forward only depending on the string width */
if ( LCDFN(scroll_info).bidir_limit ) {
s->bidir = w < (vp->width) *
@@ -676,3 +844,128 @@ void LCDFN(nine_segment_bmp)(const struct bitmap* bm, int x, int y,
LCDFN(bmp_part)(bm, bm->width - corner_w, bm->width - corner_h,
width - corner_w, height - corner_h, corner_w, corner_h);
}
+
+void LCDFN(drawpixel)(int x, int y)
+{
+ struct viewport *vp = LCDFN(current_viewport);
+ if (clip_viewport_pixel(vp, &x, &y))
+ {
+#if LCDM(DEPTH) >= 8
+ LCDFN(fastpixelfunc_type) *pfunc = LCDFN(fastpixelfuncs)[vp->drawmode];
+ void *(*fbaddr)(int x, int y) = vp->buffer->get_address_fn;
+ pfunc(fbaddr(x, y));
+#else
+ LCDFN(pixelfunc_type) *pfunc = LCDFN(pixelfuncs)[vp->drawmode];
+ pfunc(x, y);
+#endif
+ }
+}
+
+void LCDFN(drawline)(int x1, int y1, int x2, int y2)
+{
+ struct viewport *vp = LCDFN(current_viewport);
+ int numpixels;
+ int i;
+ int deltax, deltay;
+ int d, dinc1, dinc2;
+ int x, xinc1, xinc2;
+ int y, yinc1, yinc2;
+
+ deltay = abs(y2 - y1);
+ if (deltay == 0)
+ {
+ lcd_hline(x1, x2, y1);
+ return;
+ }
+
+ deltax = abs(x2 - x1);
+ if (deltax == 0)
+ {
+ lcd_vline(x1, y1, y2);
+ return;
+ }
+
+ xinc2 = 1;
+ yinc2 = 1;
+
+ if (deltax >= deltay)
+ {
+ numpixels = deltax;
+ d = 2 * deltay - deltax;
+ dinc1 = deltay * 2;
+ dinc2 = (deltay - deltax) * 2;
+ xinc1 = 1;
+ yinc1 = 0;
+ }
+ else
+ {
+ numpixels = deltay;
+ d = 2 * deltax - deltay;
+ dinc1 = deltax * 2;
+ dinc2 = (deltax - deltay) * 2;
+ xinc1 = 0;
+ yinc1 = 1;
+ }
+ numpixels++; /* include endpoints */
+
+ if (x1 > x2)
+ {
+ xinc1 = -xinc1;
+ xinc2 = -xinc2;
+ }
+
+ if (y1 > y2)
+ {
+ yinc1 = -yinc1;
+ yinc2 = -yinc2;
+ }
+
+ x = x1;
+ y = y1;
+
+#if LCDM(DEPTH) >= 8
+ LCDFN(fastpixelfunc_type) *pfunc = LCDFN(fastpixelfuncs)[vp->drawmode];
+ void *(*fbaddr)(int x, int y) = vp->buffer->get_address_fn;
+#else
+ LCDFN(pixelfunc_type) *pfunc = LCDFN(pixelfuncs)[vp->drawmode];
+#endif
+
+ for (i = 0; i < numpixels; i++)
+ {
+ if (x >= 0 && y >= 0 && x < vp->width && y < vp->height)
+ {
+#if LCDM(DEPTH) >= 8
+ pfunc(fbaddr(x + vp->x, y + vp->y));
+#else
+ pfunc(x + vp->x, y + vp->y);
+#endif
+ }
+
+ if (d < 0)
+ {
+ d += dinc1;
+ x += xinc1;
+ y += yinc1;
+ }
+ else
+ {
+ d += dinc2;
+ x += xinc2;
+ y += yinc2;
+ }
+ }
+}
+
+void LCDFN(drawrect)(int x, int y, int width, int height)
+{
+ if ((width <= 0) || (height <= 0))
+ return;
+
+ int x2 = x + width - 1;
+ int y2 = y + height - 1;
+
+ LCDFN(vline)(x, y, y2);
+ LCDFN(vline)(x2, y, y2);
+ LCDFN(hline)(x, x2, y);
+ LCDFN(hline)(x, x2, y2);
+}
diff --git a/firmware/drivers/lcd-color-common.c b/firmware/drivers/lcd-color-common.c
index 935f4e59dd..5f83160dab 100644
--- a/firmware/drivers/lcd-color-common.c
+++ b/firmware/drivers/lcd-color-common.c
@@ -61,6 +61,7 @@ static struct viewport default_vp =
.y = 0,
.width = LCD_WIDTH,
.height = LCD_HEIGHT,
+ .flags = 0,
.font = FONT_SYSFIXED,
.drawmode = DRMODE_SOLID,
.buffer = NULL,
@@ -75,7 +76,7 @@ static void *lcd_frameaddress_default(int x, int y)
/* the default expects a buffer the same size as the screen */
struct frame_buffer_t *fb = lcd_current_viewport->buffer;
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
size_t element = (x * LCD_NATIVE_STRIDE(fb->stride)) + y;
#else
size_t element = (y * LCD_NATIVE_STRIDE(fb->stride)) + x;
@@ -110,16 +111,6 @@ void lcd_clear_display(void)
/*** parameter handling ***/
-void lcd_set_drawmode(int mode)
-{
- lcd_current_viewport->drawmode = mode & (DRMODE_SOLID|DRMODE_INVERSEVID);
-}
-
-int lcd_get_drawmode(void)
-{
- return lcd_current_viewport->drawmode;
-}
-
void lcd_set_foreground(unsigned color)
{
lcd_current_viewport->fg_pattern = color;
@@ -140,37 +131,6 @@ unsigned lcd_get_background(void)
return lcd_current_viewport->bg_pattern;
}
-void lcd_set_drawinfo(int mode, unsigned fg_color, unsigned bg_color)
-{
- lcd_set_drawmode(mode);
- lcd_current_viewport->fg_pattern = fg_color;
- lcd_current_viewport->bg_pattern = bg_color;
-}
-
-int lcd_getwidth(void)
-{
- return lcd_current_viewport->width;
-}
-
-int lcd_getheight(void)
-{
- return lcd_current_viewport->height;
-}
-
-void lcd_setfont(int newfont)
-{
- lcd_current_viewport->font = newfont;
-}
-
-int lcd_getfont(void)
-{
- return lcd_current_viewport->font;
-}
-
-int lcd_getstringsize(const unsigned char *str, int *w, int *h)
-{
- return font_getstringsize(str, w, h, lcd_current_viewport->font);
-}
void lcd_set_backdrop(fb_data* backdrop)
{
@@ -192,124 +152,6 @@ fb_data* lcd_get_backdrop(void)
return lcd_backdrop;
}
-/* Set a single pixel */
-void lcd_drawpixel(int x, int y)
-{
- if ( ((unsigned)x < (unsigned)lcd_current_viewport->width)
- && ((unsigned)y < (unsigned)lcd_current_viewport->height)
-#if defined(HAVE_VIEWPORT_CLIP)
- && ((unsigned)x < (unsigned)LCD_WIDTH)
- && ((unsigned)y < (unsigned)LCD_HEIGHT)
-#endif
- )
- lcd_fastpixelfuncs[lcd_current_viewport->drawmode](FBADDR(lcd_current_viewport->x+x, lcd_current_viewport->y+y));
-}
-
-/* Draw a line */
-void lcd_drawline(int x1, int y1, int x2, int y2)
-{
- int numpixels;
- int i;
- int deltax, deltay;
- int d, dinc1, dinc2;
- int x, xinc1, xinc2;
- int y, yinc1, yinc2;
- lcd_fastpixelfunc_type *pfunc = lcd_fastpixelfuncs[lcd_current_viewport->drawmode];
-
- deltay = abs(y2 - y1);
- if (deltay == 0)
- {
- /* DEBUGF("lcd_drawline() called for horizontal line - optimisation.\n"); */
- lcd_hline(x1, x2, y1);
- return;
- }
- deltax = abs(x2 - x1);
- if (deltax == 0)
- {
- /* DEBUGF("lcd_drawline() called for vertical line - optimisation.\n"); */
- lcd_vline(x1, y1, y2);
- return;
- }
- xinc2 = 1;
- yinc2 = 1;
-
- if (deltax >= deltay)
- {
- numpixels = deltax;
- d = 2 * deltay - deltax;
- dinc1 = deltay * 2;
- dinc2 = (deltay - deltax) * 2;
- xinc1 = 1;
- yinc1 = 0;
- }
- else
- {
- numpixels = deltay;
- d = 2 * deltax - deltay;
- dinc1 = deltax * 2;
- dinc2 = (deltax - deltay) * 2;
- xinc1 = 0;
- yinc1 = 1;
- }
- numpixels++; /* include endpoints */
-
- if (x1 > x2)
- {
- xinc1 = -xinc1;
- xinc2 = -xinc2;
- }
-
- if (y1 > y2)
- {
- yinc1 = -yinc1;
- yinc2 = -yinc2;
- }
-
- x = x1;
- y = y1;
-
- for (i = 0; i < numpixels; i++)
- {
- if ( ((unsigned)x < (unsigned)lcd_current_viewport->width)
- && ((unsigned)y < (unsigned)lcd_current_viewport->height)
-#if defined(HAVE_VIEWPORT_CLIP)
- && ((unsigned)x < (unsigned)LCD_WIDTH)
- && ((unsigned)y < (unsigned)LCD_HEIGHT)
-#endif
- )
- pfunc(FBADDR(x + lcd_current_viewport->x, y + lcd_current_viewport->y));
-
- if (d < 0)
- {
- d += dinc1;
- x += xinc1;
- y += yinc1;
- }
- else
- {
- d += dinc2;
- x += xinc2;
- y += yinc2;
- }
- }
-}
-
-/* Draw a rectangular box */
-void lcd_drawrect(int x, int y, int width, int height)
-{
- if ((width <= 0) || (height <= 0))
- return;
-
- int x2 = x + width - 1;
- int y2 = y + height - 1;
-
- lcd_vline(x, y, y2);
- lcd_vline(x2, y, y2);
- lcd_hline(x, x2, y);
- lcd_hline(x, x2, y2);
-}
-
-
/* Draw a full native bitmap */
void lcd_bitmap(const fb_data *src, int x, int y, int width, int height)
{
@@ -397,9 +239,10 @@ __attribute__((weak))
#endif
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)
+ int dst_x, int dst_y, int width, int height)
{
const unsigned char *ysrc, *usrc, *vsrc;
+
int linecounter;
fb_data *dst, *row_end;
long z;
@@ -409,10 +252,10 @@ void lcd_blit_yuv(unsigned char * const src[3],
linecounter = height >> 1;
#if LCD_WIDTH >= LCD_HEIGHT
- dst = FBADDR(x, y);
+ dst = FBADDR(dst_x, dst_y);
row_end = dst + width;
#else
- dst = FBADDR(LCD_WIDTH - y - 1, x);
+ dst = FBADDR(LCD_WIDTH - dst_y - 1, dst_x);
row_end = dst + LCD_WIDTH * width;
#endif
@@ -428,10 +271,10 @@ void lcd_blit_yuv(unsigned char * const src[3],
do
{
+ int y, cb, cr, rv, guv, bu, r, g, b;
+
do
{
- int y, cb, cr, rv, guv, bu, r, g, b;
-
y = YFAC*(*ysrc++ - 16);
cb = *usrc++ - 128;
cr = *vsrc++ - 128;
@@ -495,8 +338,6 @@ void lcd_blit_yuv(unsigned char * const src[3],
do
{
- int y, cb, cr, rv, guv, bu, r, g, b;
-
y = YFAC*(*ysrc++ - 16);
cb = *usrc++ - 128;
cr = *vsrc++ - 128;
@@ -561,9 +402,9 @@ void lcd_blit_yuv(unsigned char * const src[3],
while (--linecounter > 0);
#if LCD_WIDTH >= LCD_HEIGHT
- lcd_update_rect(x, y, width, height);
+ lcd_update_rect(dst_x, dst_y, width, height);
#else
- lcd_update_rect(LCD_WIDTH - y - height, x, height, width);
+ lcd_update_rect(LCD_WIDTH - dst_y - height, dst_x, height, width);
#endif
}
@@ -589,7 +430,7 @@ void lcd_gradient_fillrect_part(int x, int y, int width, int height,
x1 = x;
x2 = x + width;
- if (height == 0) return;
+ if (height <= 0) return;
step_mul = (1 << 16) / src_height;
int h_r = RGB_UNPACK_RED(start_rgb);
diff --git a/firmware/drivers/lcd-scroll.c b/firmware/drivers/lcd-scroll.c
index d8bfd72dde..26b15732cd 100644
--- a/firmware/drivers/lcd-scroll.c
+++ b/firmware/drivers/lcd-scroll.c
@@ -64,8 +64,8 @@ void LCDFN(scroll_stop_viewport_rect)(const struct viewport *vp, int x, int y, i
struct scrollinfo *s = &LCDFN(scroll_info).scroll[i];
/* check if the specified area crosses the viewport in some way */
if (s->vp == vp
- && (x < (s->x+s->width) && (x+width) >= s->x)
- && (y < (s->y+s->height) && (y+height) >= s->y))
+ && (x < (s->x+s->width) && (x+width) > s->x)
+ && (y < (s->y+s->height) && (y+height) > s->y))
{
/* inform scroller about end of scrolling */
s->line = NULL;
@@ -180,15 +180,14 @@ bool LCDFN(scroll_now)(struct scrollinfo *s)
return ended;
}
-#if !defined(BOOTLOADER) || defined(HAVE_REMOTE_LCD)
+#if !defined(BOOTLOADER)
static void LCDFN(scroll_worker)(void)
{
int index;
bool makedelay;
- bool is_default;
struct scroll_screen_info *si = &LCDFN(scroll_info);
struct scrollinfo *s;
- struct viewport *vp;
+ struct viewport *oldvp;
int step;
for ( index = 0; index < si->lines; index++ )
@@ -206,8 +205,7 @@ static void LCDFN(scroll_worker)(void)
* is unaware of the swapped viewports. the vp must
* be switched early so that lcd_getstringsize() picks the
* correct font */
- vp = LCDFN(get_viewport)(&is_default);
- LCDFN(set_viewport_ex)(s->vp, 0); /* don't mark the last vp as dirty */
+ oldvp = LCDFN(set_viewport_ex)(s->vp, 0); /* don't mark the last vp as dirty */
makedelay = false;
step = si->step;
@@ -220,7 +218,7 @@ static void LCDFN(scroll_worker)(void)
/* put the line onto the display now */
makedelay = LCDFN(scroll_now(s));
- LCDFN(set_viewport_ex)(vp, 0); /* don't mark the last vp as dirty */
+ LCDFN(set_viewport_ex)(oldvp, 0); /* don't mark the last vp as dirty */
if (makedelay)
s->start_tick += si->delay + si->ticks;
diff --git a/firmware/drivers/libertas/firmware/LICENCE.Marvell b/firmware/drivers/libertas/firmware/LICENCE.Marvell
new file mode 100644
index 0000000000..fdf4cda9c0
--- /dev/null
+++ b/firmware/drivers/libertas/firmware/LICENCE.Marvell
@@ -0,0 +1,22 @@
+Copyright © 2019. Marvell International Ltd. All rights reserved.
+
+Redistribution and use in binary form is permitted provided that the following
+conditions are met:
+
+1. Redistributions must reproduce the above copyright notice, this list of
+conditions and the following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+2. Redistribution and use shall be used only with Marvell silicon products.
+Any other use, reproduction, modification, translation, or compilation of the
+Software is prohibited.
+
+3. No reverse engineering, decompilation, or disassembly is permitted.
+
+TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THIS SOFTWARE IS PROVIDED
+“AS IS” WITHOUT WARRANTY OF ANY KIND, INCLUDING, WITHOUT LIMITATION, ANY EXPRESS
+OR IMPLIED WARRANTIES OF MERCHANTABILITY, ACCURACY, FITNESS OR SUFFICIENCY FOR A
+PARTICULAR PURPOSE, SATISFACTORY QUALITY, CORRESPONDENCE WITH DESCRIPTION, QUIET
+ENJOYMENT OR NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY RIGHTS.
+MARVELL, ITS AFFILIATES AND THEIR SUPPLIERS DISCLAIM ANY WARRANTY THAT THE
+DELIVERABLES WILL OPERATE WITHOUT INTERRUPTION OR BE ERROR-FREE.
diff --git a/firmware/drivers/libertas/firmware/gspi8686_v9.bin b/firmware/drivers/libertas/firmware/gspi8686_v9.bin
new file mode 100644
index 0000000000..58dc03d30e
--- /dev/null
+++ b/firmware/drivers/libertas/firmware/gspi8686_v9.bin
Binary files differ
diff --git a/firmware/drivers/libertas/firmware/gspi8686_v9_helper.bin b/firmware/drivers/libertas/firmware/gspi8686_v9_helper.bin
new file mode 100644
index 0000000000..424cfb386c
--- /dev/null
+++ b/firmware/drivers/libertas/firmware/gspi8686_v9_helper.bin
Binary files differ
diff --git a/firmware/drivers/libertas/if_spi.c b/firmware/drivers/libertas/if_spi.c
new file mode 100644
index 0000000000..be564cc64f
--- /dev/null
+++ b/firmware/drivers/libertas/if_spi.c
@@ -0,0 +1,674 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 by Tomasz Moń
+ * Ported from Linux libertas driver
+ * Copyright 2008 Analog Devices Inc.
+ * Authors:
+ * Andrey Yurovsky <andrey@cozybit.com>
+ * Colin McCabe <colin@cozybit.com>
+ * Inspired by if_sdio.c, Copyright 2007-2008 Pierre Ossman
+ *
+ * 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"
+/*#define LOGF_ENABLE*/
+#include "logf.h"
+#include "errno.h"
+#include "file.h"
+#include "panic.h"
+#include "system.h"
+#include "tick.h"
+#include <stddef.h>
+#include "if_spi.h"
+#include "if_spi_drv.h"
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#define DIV_ROUND_UP(n,d) (((n) + (d) - 1)/(d))
+
+struct if_spi_card
+{
+ /* The card ID and card revision, as reported by the hardware. */
+ uint16_t card_id;
+ uint8_t card_rev;
+
+ unsigned long spu_port_delay;
+ unsigned long spu_reg_delay;
+
+ uint8_t cmd_buffer[IF_SPI_CMD_BUF_SIZE];
+};
+
+#define MODEL_8686 0x0b
+
+static const struct
+{
+ uint16_t model;
+ const char *helper;
+ const char *main;
+}
+fw_table[] =
+{
+ { MODEL_8686, ROCKBOX_DIR"/libertas/gspi8686_v9_helper.bin", ROCKBOX_DIR"/libertas/gspi8686_v9.bin" },
+ { 0, NULL, NULL }
+};
+
+
+/*
+ * SPI Interface Unit Routines
+ *
+ * The SPU sits between the host and the WLAN module.
+ * All communication with the firmware is through SPU transactions.
+ *
+ * First we have to put a SPU register name on the bus. Then we can
+ * either read from or write to that register.
+ *
+ */
+
+static void spu_transaction_init(struct if_spi_card *card)
+{
+ (void)card;
+ /* Linux delays 400 ns if spu_transaction_finish() was called
+ * within the same jiffy. As we don't have jiffy counter nor
+ * nanosecond delays, simply delay for 1 us. This currently
+ * does not really matter as this driver simply loads firmware.
+ */
+ udelay(1);
+ libertas_spi_cs(0); /* assert CS */
+}
+
+static void spu_transaction_finish(struct if_spi_card *card)
+{
+ (void)card;
+ libertas_spi_cs(1); /* drop CS */
+}
+
+/*
+ * Write out a byte buffer to an SPI register,
+ * using a series of 16-bit transfers.
+ */
+static int spu_write(struct if_spi_card *card, uint16_t reg, const uint8_t *buf, int len)
+{
+ int err = 0;
+ uint8_t reg_out[2];
+
+ /* You must give an even number of bytes to the SPU, even if it
+ * doesn't care about the last one. */
+ if (len & 0x1)
+ panicf("Odd length in spu_write()");
+
+ reg |= IF_SPI_WRITE_OPERATION_MASK;
+ reg_out[0] = (reg & 0x00FF);
+ reg_out[1] = (reg & 0xFF00) >> 8;
+
+ spu_transaction_init(card);
+ libertas_spi_tx(reg_out, sizeof(reg_out));
+ libertas_spi_tx(buf, len);
+ spu_transaction_finish(card);
+ return err;
+}
+
+static inline int spu_write_u16(struct if_spi_card *card, uint16_t reg, uint16_t val)
+{
+ uint8_t buf[2];
+ buf[0] = (val & 0x00FF);
+ buf[1] = (val & 0xFF00) >> 8;
+ return spu_write(card, reg, buf, sizeof(buf));
+}
+
+static inline int spu_reg_is_port_reg(uint16_t reg)
+{
+ switch (reg)
+ {
+ case IF_SPI_IO_RDWRPORT_REG:
+ case IF_SPI_CMD_RDWRPORT_REG:
+ case IF_SPI_DATA_RDWRPORT_REG:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int spu_read(struct if_spi_card *card, uint16_t reg, uint8_t *buf, int len)
+{
+ unsigned int delay;
+ int err = 0;
+ uint8_t reg_out[2];
+
+ /*
+ * You must take an even number of bytes from the SPU, even if you
+ * don't care about the last one.
+ */
+ if (len & 0x1)
+ panicf("Odd length in spu_read()");
+
+ reg |= IF_SPI_READ_OPERATION_MASK;
+ reg_out[0] = (reg & 0x00FF);
+ reg_out[1] = (reg & 0xFF00) >> 8;
+
+ spu_transaction_init(card);
+ libertas_spi_tx(reg_out, sizeof(reg_out));
+
+ delay = spu_reg_is_port_reg(reg) ? card->spu_port_delay : card->spu_reg_delay;
+ /* Busy-wait while the SPU fills the FIFO */
+ delay = DIV_ROUND_UP((100 + (delay * 10)), 1000);
+ if (delay < 1000)
+ udelay(delay);
+ else
+ mdelay(DIV_ROUND_UP(delay, 1000));
+
+ libertas_spi_rx(buf, len);
+ spu_transaction_finish(card);
+ return err;
+}
+
+/* Read 16 bits from an SPI register */
+static inline int spu_read_u16(struct if_spi_card *card, uint16_t reg, uint16_t *val)
+{
+ uint8_t buf[2];
+ int ret;
+
+ ret = spu_read(card, reg, buf, sizeof(buf));
+ if (ret == 0)
+ *val = buf[0] | (buf[1] << 8);
+
+ return ret;
+}
+
+/*
+ * Read 32 bits from an SPI register.
+ * The low 16 bits are read first.
+ */
+static int spu_read_u32(struct if_spi_card *card, uint16_t reg, uint32_t *val)
+{
+ uint8_t buf[4];
+ int err;
+
+ err = spu_read(card, reg, buf, sizeof(buf));
+ if (!err)
+ *val = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
+ return err;
+}
+
+/*
+ * Keep reading 16 bits from an SPI register until you get the correct result.
+ *
+ * If mask = 0, the correct result is any non-zero number.
+ * If mask != 0, the correct result is any number where
+ * number & target_mask == target
+ *
+ * Returns -ETIMEDOUT if a five seconds passes without the correct result.
+ */
+static int spu_wait_for_u16(struct if_spi_card *card, uint16_t reg,
+ uint16_t target_mask, uint16_t target)
+{
+ int err;
+ unsigned long timeout = current_tick + 5*HZ;
+ while (1)
+ {
+ uint16_t val;
+ err = spu_read_u16(card, reg, &val);
+ if (err)
+ return err;
+ if (target_mask)
+ {
+ if ((val & target_mask) == target)
+ return 0;
+ }
+ else
+ {
+ if (val)
+ return 0;
+ }
+ udelay(100);
+ if (TIME_AFTER(current_tick, timeout))
+ {
+ logf("%s: timeout with val=%02x, target_mask=%02x, target=%02x",
+ __func__, val, target_mask, target);
+ return -ETIMEDOUT;
+ }
+ }
+}
+
+/*
+ * Read 16 bits from an SPI register until you receive a specific value.
+ * Returns -ETIMEDOUT if a 4 tries pass without success.
+ */
+static int spu_wait_for_u32(struct if_spi_card *card, uint32_t reg, uint32_t target)
+{
+ int err, try;
+ for (try = 0; try < 4; ++try)
+ {
+ uint32_t val = 0;
+ err = spu_read_u32(card, reg, &val);
+ if (err)
+ return err;
+ if (val == target)
+ return 0;
+ mdelay(100);
+ }
+ return -ETIMEDOUT;
+}
+
+static int spu_set_interrupt_mode(struct if_spi_card *card,
+ int suppress_host_int,
+ int auto_int)
+{
+ int err = 0;
+
+ /*
+ * We can suppress a host interrupt by clearing the appropriate
+ * bit in the "host interrupt status mask" register
+ */
+ if (suppress_host_int) {
+ err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, 0);
+ if (err)
+ return err;
+ } else {
+ err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG,
+ IF_SPI_HISM_TX_DOWNLOAD_RDY |
+ IF_SPI_HISM_RX_UPLOAD_RDY |
+ IF_SPI_HISM_CMD_DOWNLOAD_RDY |
+ IF_SPI_HISM_CARDEVENT |
+ IF_SPI_HISM_CMD_UPLOAD_RDY);
+ if (err)
+ return err;
+ }
+
+ /*
+ * If auto-interrupts are on, the completion of certain transactions
+ * will trigger an interrupt automatically. If auto-interrupts
+ * are off, we need to set the "Card Interrupt Cause" register to
+ * trigger a card interrupt.
+ */
+ if (auto_int) {
+ err = spu_write_u16(card, IF_SPI_HOST_INT_CTRL_REG,
+ IF_SPI_HICT_TX_DOWNLOAD_OVER_AUTO |
+ IF_SPI_HICT_RX_UPLOAD_OVER_AUTO |
+ IF_SPI_HICT_CMD_DOWNLOAD_OVER_AUTO |
+ IF_SPI_HICT_CMD_UPLOAD_OVER_AUTO);
+ if (err)
+ return err;
+ } else {
+ err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, 0);
+ if (err)
+ return err;
+ }
+ return err;
+}
+
+static int spu_get_chip_revision(struct if_spi_card *card,
+ uint16_t *card_id, uint8_t *card_rev)
+{
+ int err = 0;
+ uint32_t dev_ctrl;
+ err = spu_read_u32(card, IF_SPI_DEVICEID_CTRL_REG, &dev_ctrl);
+ if (err)
+ return err;
+ *card_id = IF_SPI_DEVICEID_CTRL_REG_TO_CARD_ID(dev_ctrl);
+ *card_rev = IF_SPI_DEVICEID_CTRL_REG_TO_CARD_REV(dev_ctrl);
+ return err;
+}
+
+static int spu_set_bus_mode(struct if_spi_card *card, uint16_t mode)
+{
+ int err = 0;
+ uint16_t rval;
+ /* set bus mode */
+ err = spu_write_u16(card, IF_SPI_SPU_BUS_MODE_REG, mode);
+ if (err)
+ return err;
+ /* Check that we were able to read back what we just wrote. */
+ err = spu_read_u16(card, IF_SPI_SPU_BUS_MODE_REG, &rval);
+ if (err)
+ return err;
+ if ((rval & 0xF) != mode)
+ {
+ logf("Can't read bus mode register");
+ return -EIO;
+ }
+ return 0;
+}
+
+static int spu_init(struct if_spi_card *card)
+{
+ int err = 0;
+ uint32_t delay;
+
+ err = spu_set_bus_mode(card,
+ IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING |
+ IF_SPI_BUS_MODE_DELAY_METHOD_TIMED |
+ IF_SPI_BUS_MODE_16_BIT_ADDRESS_16_BIT_DATA);
+ if (err)
+ return err;
+ card->spu_port_delay = 1000;
+ card->spu_reg_delay = 1000;
+ err = spu_read_u32(card, IF_SPI_DELAY_READ_REG, &delay);
+ if (err)
+ return err;
+ card->spu_port_delay = delay & 0x0000ffff;
+ card->spu_reg_delay = (delay & 0xffff0000) >> 16;
+
+ logf("Initialized SPU unit. "
+ "spu_port_delay=0x%04lx, spu_reg_delay=0x%04lx",
+ card->spu_port_delay, card->spu_reg_delay);
+ return err;
+}
+
+
+/*
+ * Firmware Loading
+ */
+
+static int if_spi_prog_helper_firmware(struct if_spi_card *card, int fd)
+{
+ int err = 0;
+ int bytes_read;
+ uint8_t *temp = card->cmd_buffer;
+
+ err = spu_set_interrupt_mode(card, 1, 0);
+ if (err)
+ goto out;
+
+ /* Load helper firmware image */
+ while ((bytes_read = read(fd, temp, HELPER_FW_LOAD_CHUNK_SZ)) > 0)
+ {
+ /*
+ * Scratch pad 1 should contain the number of bytes we
+ * want to download to the firmware
+ */
+ err = spu_write_u16(card, IF_SPI_SCRATCH_1_REG,
+ HELPER_FW_LOAD_CHUNK_SZ);
+ if (err)
+ goto out;
+
+ err = spu_wait_for_u16(card, IF_SPI_HOST_INT_STATUS_REG,
+ IF_SPI_HIST_CMD_DOWNLOAD_RDY,
+ IF_SPI_HIST_CMD_DOWNLOAD_RDY);
+ if (err)
+ goto out;
+
+ /*
+ * Feed the data into the command read/write port reg
+ * in chunks of 64 bytes
+ */
+ memset(temp + bytes_read, 0, HELPER_FW_LOAD_CHUNK_SZ - bytes_read);
+ mdelay(10);
+ err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG,
+ temp, HELPER_FW_LOAD_CHUNK_SZ);
+ if (err)
+ goto out;
+
+ /* Interrupt the boot code */
+ err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0);
+ if (err)
+ goto out;
+ err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG,
+ IF_SPI_CIC_CMD_DOWNLOAD_OVER);
+ if (err)
+ goto out;
+ }
+
+ /*
+ * Once the helper / single stage firmware download is complete,
+ * write 0 to scratch pad 1 and interrupt the
+ * bootloader. This completes the helper download.
+ */
+ err = spu_write_u16(card, IF_SPI_SCRATCH_1_REG, FIRMWARE_DNLD_OK);
+ if (err)
+ goto out;
+ err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0);
+ if (err)
+ goto out;
+ err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG,
+ IF_SPI_CIC_CMD_DOWNLOAD_OVER);
+out:
+ if (err)
+ logf("failed to load helper firmware (err=%d)", err);
+
+ return err;
+}
+
+/*
+ * Returns the length of the next packet the firmware expects us to send.
+ * Sets crc_err if the previous transfer had a CRC error.
+ */
+static int if_spi_prog_main_firmware_check_len(struct if_spi_card *card,
+ int *crc_err)
+{
+ uint16_t len;
+ int err = 0;
+
+ /*
+ * wait until the host interrupt status register indicates
+ * that we are ready to download
+ */
+ err = spu_wait_for_u16(card, IF_SPI_HOST_INT_STATUS_REG,
+ IF_SPI_HIST_CMD_DOWNLOAD_RDY,
+ IF_SPI_HIST_CMD_DOWNLOAD_RDY);
+ if (err)
+ {
+ logf("timed out waiting for host_int_status");
+ return err;
+ }
+
+ /* Ask the device how many bytes of firmware it wants. */
+ err = spu_read_u16(card, IF_SPI_SCRATCH_1_REG, &len);
+ if (err)
+ return err;
+
+ if (len > IF_SPI_CMD_BUF_SIZE)
+ {
+ logf("firmware load device requested a larger transfer than we are prepared to handle (len = %d)",
+ len);
+ return -EIO;
+ }
+ if (len & 0x1) {
+ logf("%s: crc error", __func__);
+ len &= ~0x1;
+ *crc_err = 1;
+ } else
+ *crc_err = 0;
+
+ return len;
+}
+
+static int if_spi_prog_main_firmware(struct if_spi_card *card, int fd)
+{
+ int len;
+ int bytes_read = 0, crc_err = 0, err = 0;
+ uint16_t num_crc_errs;
+
+ err = spu_set_interrupt_mode(card, 1, 0);
+ if (err)
+ goto out;
+
+ err = spu_wait_for_u16(card, IF_SPI_SCRATCH_1_REG, 0, 0);
+ if (err)
+ {
+ logf("%s: timed out waiting for initial scratch reg = 0", __func__);
+ goto out;
+ }
+
+ num_crc_errs = 0;
+ while ((len = if_spi_prog_main_firmware_check_len(card, &crc_err)))
+ {
+ if (len < 0)
+ {
+ err = len;
+ goto out;
+ }
+ if (crc_err)
+ {
+ /* Previous transfer failed. */
+ if (++num_crc_errs > MAX_MAIN_FW_LOAD_CRC_ERR)
+ {
+ logf("Too many CRC errors encountered in firmware load.");
+ err = -EIO;
+ goto out;
+ }
+
+ /* Rewind so we read back the data from previous transfer */
+ lseek(fd, -bytes_read, SEEK_CUR);
+ }
+
+ bytes_read = read(fd, card->cmd_buffer, len);
+ if (bytes_read < 0)
+ {
+ /*
+ * If there are no more bytes left, we would normally
+ * expect to have terminated with len = 0
+ */
+ logf("Firmware load wants more bytes than we have to offer.");
+ break;
+ }
+ else if (bytes_read < len)
+ {
+ memset(card->cmd_buffer + bytes_read, 0, len - bytes_read);
+ }
+
+ err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0);
+ if (err)
+ goto out;
+ err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG, card->cmd_buffer, len);
+ if (err)
+ goto out;
+ err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG,
+ IF_SPI_CIC_CMD_DOWNLOAD_OVER);
+ if (err)
+ goto out;
+ }
+ if (read(fd, card->cmd_buffer, IF_SPI_CMD_BUF_SIZE) > 0)
+ {
+ logf("firmware load wants fewer bytes than we have to offer");
+ }
+
+ /* Confirm firmware download */
+ err = spu_wait_for_u32(card, IF_SPI_SCRATCH_4_REG,
+ SUCCESSFUL_FW_DOWNLOAD_MAGIC);
+ if (err)
+ {
+ logf("failed to confirm the firmware download");
+ goto out;
+ }
+
+out:
+ if (err)
+ logf("failed to load firmware (err=%d)", err);
+
+ return err;
+}
+
+static int if_spi_init_card(struct if_spi_card *card)
+{
+ int err;
+ size_t i;
+ uint32_t scratch;
+ int fd;
+
+ err = spu_init(card);
+ if (err)
+ goto out;
+ err = spu_get_chip_revision(card, &card->card_id, &card->card_rev);
+ if (err)
+ goto out;
+
+ err = spu_read_u32(card, IF_SPI_SCRATCH_4_REG, &scratch);
+ if (err)
+ goto out;
+ if (scratch == SUCCESSFUL_FW_DOWNLOAD_MAGIC)
+ logf("Firmware is already loaded for Marvell WLAN 802.11 adapter");
+ else {
+ /* Check if we support this card */
+ for (i = 0; i < ARRAY_SIZE(fw_table); i++) {
+ if (card->card_id == fw_table[i].model)
+ break;
+ }
+ if (i == ARRAY_SIZE(fw_table)) {
+ logf("Unsupported chip_id: 0x%02x", card->card_id);
+ err = -ENODEV;
+ goto out;
+ }
+
+ logf("Initializing FW for Marvell WLAN 802.11 adapter "
+ "(chip_id = 0x%04x, chip_rev = 0x%02x)",
+ card->card_id, card->card_rev);
+
+ fd = open(fw_table[i].helper, O_RDONLY);
+ if (fd >= 0)
+ {
+ err = if_spi_prog_helper_firmware(card, fd);
+ close(fd);
+ if (err)
+ goto out;
+ }
+ else
+ {
+ logf("failed to find firmware helper (%s)", fw_table[i].helper);
+ err = -ENOENT;
+ goto out;
+ }
+
+ fd = open(fw_table[i].main, O_RDONLY);
+ if (fd >= 0)
+ {
+ err = if_spi_prog_main_firmware(card, fd);
+ close(fd);
+ if (err)
+ goto out;
+ }
+ else
+ {
+ logf("failed to find firmware (%s)", fw_table[i].main);
+ err = -ENOENT;
+ goto out;
+ }
+
+ logf("loaded FW for Marvell WLAN 802.11 adapter");
+ }
+
+ err = spu_set_interrupt_mode(card, 0, 1);
+ if (err)
+ goto out;
+
+out:
+ return err;
+}
+
+void wifi_init(void) INIT_ATTR
+{
+#if 0
+ static struct if_spi_card card;
+ libertas_spi_init();
+ libertas_spi_pd(1);
+ libertas_spi_reset(1);
+ mdelay(100);
+ if (!if_spi_init_card(&card))
+ {
+ /* TODO: Configure card and enter deep sleep */
+ }
+ else
+#else
+ libertas_spi_init();
+ (void)if_spi_init_card;
+#endif
+ {
+ /* Keep the lines in lowest power configuration */
+ libertas_spi_pd(0);
+ libertas_spi_reset(1);
+ libertas_spi_cs(1);
+ }
+}
diff --git a/firmware/drivers/libertas/if_spi.h b/firmware/drivers/libertas/if_spi.h
new file mode 100644
index 0000000000..bfca12981a
--- /dev/null
+++ b/firmware/drivers/libertas/if_spi.h
@@ -0,0 +1,215 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 by Tomasz Moń
+ * Ported from Linux libertas driver
+ * Copyright 2008 Analog Devices Inc.
+ * Authors:
+ * Andrey Yurovsky <andrey@cozybit.com>
+ * Colin McCabe <colin@cozybit.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef _LBS_IF_SPI_H_
+#define _LBS_IF_SPI_H_
+
+#define IPFIELD_ALIGN_OFFSET 2
+#define IF_SPI_CMD_BUF_SIZE 2400
+
+/***************** Firmware *****************/
+
+#define IF_SPI_FW_NAME_MAX 30
+
+#define MAX_MAIN_FW_LOAD_CRC_ERR 10
+
+/* Chunk size when loading the helper firmware */
+#define HELPER_FW_LOAD_CHUNK_SZ 64
+
+/* Value to write to indicate end of helper firmware dnld */
+#define FIRMWARE_DNLD_OK 0x0000
+
+/* Value to check once the main firmware is downloaded */
+#define SUCCESSFUL_FW_DOWNLOAD_MAGIC 0x88888888
+
+/***************** SPI Interface Unit *****************/
+/* Masks used in SPI register read/write operations */
+#define IF_SPI_READ_OPERATION_MASK 0x0
+#define IF_SPI_WRITE_OPERATION_MASK 0x8000
+
+/* SPI register offsets. 4-byte aligned. */
+#define IF_SPI_DEVICEID_CTRL_REG 0x00 /* DeviceID controller reg */
+#define IF_SPI_IO_READBASE_REG 0x04 /* Read I/O base reg */
+#define IF_SPI_IO_WRITEBASE_REG 0x08 /* Write I/O base reg */
+#define IF_SPI_IO_RDWRPORT_REG 0x0C /* Read/Write I/O port reg */
+
+#define IF_SPI_CMD_READBASE_REG 0x10 /* Read command base reg */
+#define IF_SPI_CMD_WRITEBASE_REG 0x14 /* Write command base reg */
+#define IF_SPI_CMD_RDWRPORT_REG 0x18 /* Read/Write command port reg */
+
+#define IF_SPI_DATA_READBASE_REG 0x1C /* Read data base reg */
+#define IF_SPI_DATA_WRITEBASE_REG 0x20 /* Write data base reg */
+#define IF_SPI_DATA_RDWRPORT_REG 0x24 /* Read/Write data port reg */
+
+#define IF_SPI_SCRATCH_1_REG 0x28 /* Scratch reg 1 */
+#define IF_SPI_SCRATCH_2_REG 0x2C /* Scratch reg 2 */
+#define IF_SPI_SCRATCH_3_REG 0x30 /* Scratch reg 3 */
+#define IF_SPI_SCRATCH_4_REG 0x34 /* Scratch reg 4 */
+
+#define IF_SPI_TX_FRAME_SEQ_NUM_REG 0x38 /* Tx frame sequence number reg */
+#define IF_SPI_TX_FRAME_STATUS_REG 0x3C /* Tx frame status reg */
+
+#define IF_SPI_HOST_INT_CTRL_REG 0x40 /* Host interrupt controller reg */
+
+#define IF_SPI_CARD_INT_CAUSE_REG 0x44 /* Card interrupt cause reg */
+#define IF_SPI_CARD_INT_STATUS_REG 0x48 /* Card interrupt status reg */
+#define IF_SPI_CARD_INT_EVENT_MASK_REG 0x4C /* Card interrupt event mask */
+#define IF_SPI_CARD_INT_STATUS_MASK_REG 0x50 /* Card interrupt status mask */
+
+#define IF_SPI_CARD_INT_RESET_SELECT_REG 0x54 /* Card interrupt reset select */
+
+#define IF_SPI_HOST_INT_CAUSE_REG 0x58 /* Host interrupt cause reg */
+#define IF_SPI_HOST_INT_STATUS_REG 0x5C /* Host interrupt status reg */
+#define IF_SPI_HOST_INT_EVENT_MASK_REG 0x60 /* Host interrupt event mask */
+#define IF_SPI_HOST_INT_STATUS_MASK_REG 0x64 /* Host interrupt status mask */
+#define IF_SPI_HOST_INT_RESET_SELECT_REG 0x68 /* Host interrupt reset select */
+
+#define IF_SPI_DELAY_READ_REG 0x6C /* Delay read reg */
+#define IF_SPI_SPU_BUS_MODE_REG 0x70 /* SPU BUS mode reg */
+
+/***************** IF_SPI_DEVICEID_CTRL_REG *****************/
+#define IF_SPI_DEVICEID_CTRL_REG_TO_CARD_ID(dc) ((dc & 0xffff0000)>>16)
+#define IF_SPI_DEVICEID_CTRL_REG_TO_CARD_REV(dc) (dc & 0x000000ff)
+
+/***************** IF_SPI_HOST_INT_CTRL_REG *****************/
+/* Host Interrupt Control bit : Wake up */
+#define IF_SPI_HICT_WAKE_UP (1<<0)
+/* Host Interrupt Control bit : WLAN ready */
+#define IF_SPI_HICT_WLAN_READY (1<<1)
+/*#define IF_SPI_HICT_FIFO_FIRST_HALF_EMPTY (1<<2) */
+/*#define IF_SPI_HICT_FIFO_SECOND_HALF_EMPTY (1<<3) */
+/*#define IF_SPI_HICT_IRQSRC_WLAN (1<<4) */
+/* Host Interrupt Control bit : Tx auto download */
+#define IF_SPI_HICT_TX_DOWNLOAD_OVER_AUTO (1<<5)
+/* Host Interrupt Control bit : Rx auto upload */
+#define IF_SPI_HICT_RX_UPLOAD_OVER_AUTO (1<<6)
+/* Host Interrupt Control bit : Command auto download */
+#define IF_SPI_HICT_CMD_DOWNLOAD_OVER_AUTO (1<<7)
+/* Host Interrupt Control bit : Command auto upload */
+#define IF_SPI_HICT_CMD_UPLOAD_OVER_AUTO (1<<8)
+
+/***************** IF_SPI_CARD_INT_CAUSE_REG *****************/
+/* Card Interrupt Case bit : Tx download over */
+#define IF_SPI_CIC_TX_DOWNLOAD_OVER (1<<0)
+/* Card Interrupt Case bit : Rx upload over */
+#define IF_SPI_CIC_RX_UPLOAD_OVER (1<<1)
+/* Card Interrupt Case bit : Command download over */
+#define IF_SPI_CIC_CMD_DOWNLOAD_OVER (1<<2)
+/* Card Interrupt Case bit : Host event */
+#define IF_SPI_CIC_HOST_EVENT (1<<3)
+/* Card Interrupt Case bit : Command upload over */
+#define IF_SPI_CIC_CMD_UPLOAD_OVER (1<<4)
+/* Card Interrupt Case bit : Power down */
+#define IF_SPI_CIC_POWER_DOWN (1<<5)
+
+/***************** IF_SPI_CARD_INT_STATUS_REG *****************/
+#define IF_SPI_CIS_TX_DOWNLOAD_OVER (1<<0)
+#define IF_SPI_CIS_RX_UPLOAD_OVER (1<<1)
+#define IF_SPI_CIS_CMD_DOWNLOAD_OVER (1<<2)
+#define IF_SPI_CIS_HOST_EVENT (1<<3)
+#define IF_SPI_CIS_CMD_UPLOAD_OVER (1<<4)
+#define IF_SPI_CIS_POWER_DOWN (1<<5)
+
+/***************** IF_SPI_HOST_INT_CAUSE_REG *****************/
+#define IF_SPI_HICU_TX_DOWNLOAD_RDY (1<<0)
+#define IF_SPI_HICU_RX_UPLOAD_RDY (1<<1)
+#define IF_SPI_HICU_CMD_DOWNLOAD_RDY (1<<2)
+#define IF_SPI_HICU_CARD_EVENT (1<<3)
+#define IF_SPI_HICU_CMD_UPLOAD_RDY (1<<4)
+#define IF_SPI_HICU_IO_WR_FIFO_OVERFLOW (1<<5)
+#define IF_SPI_HICU_IO_RD_FIFO_UNDERFLOW (1<<6)
+#define IF_SPI_HICU_DATA_WR_FIFO_OVERFLOW (1<<7)
+#define IF_SPI_HICU_DATA_RD_FIFO_UNDERFLOW (1<<8)
+#define IF_SPI_HICU_CMD_WR_FIFO_OVERFLOW (1<<9)
+#define IF_SPI_HICU_CMD_RD_FIFO_UNDERFLOW (1<<10)
+
+/***************** IF_SPI_HOST_INT_STATUS_REG *****************/
+/* Host Interrupt Status bit : Tx download ready */
+#define IF_SPI_HIST_TX_DOWNLOAD_RDY (1<<0)
+/* Host Interrupt Status bit : Rx upload ready */
+#define IF_SPI_HIST_RX_UPLOAD_RDY (1<<1)
+/* Host Interrupt Status bit : Command download ready */
+#define IF_SPI_HIST_CMD_DOWNLOAD_RDY (1<<2)
+/* Host Interrupt Status bit : Card event */
+#define IF_SPI_HIST_CARD_EVENT (1<<3)
+/* Host Interrupt Status bit : Command upload ready */
+#define IF_SPI_HIST_CMD_UPLOAD_RDY (1<<4)
+/* Host Interrupt Status bit : I/O write FIFO overflow */
+#define IF_SPI_HIST_IO_WR_FIFO_OVERFLOW (1<<5)
+/* Host Interrupt Status bit : I/O read FIFO underflow */
+#define IF_SPI_HIST_IO_RD_FIFO_UNDRFLOW (1<<6)
+/* Host Interrupt Status bit : Data write FIFO overflow */
+#define IF_SPI_HIST_DATA_WR_FIFO_OVERFLOW (1<<7)
+/* Host Interrupt Status bit : Data read FIFO underflow */
+#define IF_SPI_HIST_DATA_RD_FIFO_UNDERFLOW (1<<8)
+/* Host Interrupt Status bit : Command write FIFO overflow */
+#define IF_SPI_HIST_CMD_WR_FIFO_OVERFLOW (1<<9)
+/* Host Interrupt Status bit : Command read FIFO underflow */
+#define IF_SPI_HIST_CMD_RD_FIFO_UNDERFLOW (1<<10)
+
+/***************** IF_SPI_HOST_INT_STATUS_MASK_REG *****************/
+/* Host Interrupt Status Mask bit : Tx download ready */
+#define IF_SPI_HISM_TX_DOWNLOAD_RDY (1<<0)
+/* Host Interrupt Status Mask bit : Rx upload ready */
+#define IF_SPI_HISM_RX_UPLOAD_RDY (1<<1)
+/* Host Interrupt Status Mask bit : Command download ready */
+#define IF_SPI_HISM_CMD_DOWNLOAD_RDY (1<<2)
+/* Host Interrupt Status Mask bit : Card event */
+#define IF_SPI_HISM_CARDEVENT (1<<3)
+/* Host Interrupt Status Mask bit : Command upload ready */
+#define IF_SPI_HISM_CMD_UPLOAD_RDY (1<<4)
+/* Host Interrupt Status Mask bit : I/O write FIFO overflow */
+#define IF_SPI_HISM_IO_WR_FIFO_OVERFLOW (1<<5)
+/* Host Interrupt Status Mask bit : I/O read FIFO underflow */
+#define IF_SPI_HISM_IO_RD_FIFO_UNDERFLOW (1<<6)
+/* Host Interrupt Status Mask bit : Data write FIFO overflow */
+#define IF_SPI_HISM_DATA_WR_FIFO_OVERFLOW (1<<7)
+/* Host Interrupt Status Mask bit : Data write FIFO underflow */
+#define IF_SPI_HISM_DATA_RD_FIFO_UNDERFLOW (1<<8)
+/* Host Interrupt Status Mask bit : Command write FIFO overflow */
+#define IF_SPI_HISM_CMD_WR_FIFO_OVERFLOW (1<<9)
+/* Host Interrupt Status Mask bit : Command write FIFO underflow */
+#define IF_SPI_HISM_CMD_RD_FIFO_UNDERFLOW (1<<10)
+
+/***************** IF_SPI_SPU_BUS_MODE_REG *****************/
+/* SCK edge on which the WLAN module outputs data on MISO */
+#define IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_FALLING 0x8
+#define IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING 0x0
+
+/* In a SPU read operation, there is a delay between writing the SPU
+ * register name and getting back data from the WLAN module.
+ * This can be specified in terms of nanoseconds or in terms of dummy
+ * clock cycles which the master must output before receiving a response. */
+#define IF_SPI_BUS_MODE_DELAY_METHOD_DUMMY_CLOCK 0x4
+#define IF_SPI_BUS_MODE_DELAY_METHOD_TIMED 0x0
+
+/* Some different modes of SPI operation */
+#define IF_SPI_BUS_MODE_8_BIT_ADDRESS_16_BIT_DATA 0x00
+#define IF_SPI_BUS_MODE_8_BIT_ADDRESS_32_BIT_DATA 0x01
+#define IF_SPI_BUS_MODE_16_BIT_ADDRESS_16_BIT_DATA 0x02
+#define IF_SPI_BUS_MODE_16_BIT_ADDRESS_32_BIT_DATA 0x03
+
+#endif
diff --git a/firmware/target/arm/tcc77x/c100/backlight-target.h b/firmware/drivers/libertas/if_spi_drv.h
index 7ae71022f0..6c8b5c3d92 100644
--- a/firmware/target/arm/tcc77x/c100/backlight-target.h
+++ b/firmware/drivers/libertas/if_spi_drv.h
@@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Copyright (C) 2007 by Dave Chapman
+ * Copyright (C) 2021 by Tomasz Moń
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -18,23 +18,17 @@
* KIND, either express or implied.
*
****************************************************************************/
-#ifndef BACKLIGHT_TARGET_H
-#define BACKLIGHT_TARGET_H
-#include "tcc77x.h"
+#ifndef LIBERTAS_IF_SPI_DRV
+#define LIBERTAS_IF_SPI_DRV
-#define backlight_hw_init() true
+#include <stdint.h>
-static inline void backlight_hw_on(void)
-{
- /* Enable backlight */
- GPIOE |= 0x2;
-}
-
-static inline void backlight_hw_off(void)
-{
- /* Disable backlight */
- GPIOE &= ~0x2;
-}
+void libertas_spi_init(void);
+void libertas_spi_reset(int high);
+void libertas_spi_pd(int high);
+void libertas_spi_cs(int high);
+void libertas_spi_tx(const uint8_t *buf, int len);
+void libertas_spi_rx(uint8_t *buf, int len);
#endif
diff --git a/firmware/drivers/m66591.c b/firmware/drivers/m66591.c
index d09b269f33..822585d882 100644
--- a/firmware/drivers/m66591.c
+++ b/firmware/drivers/m66591.c
@@ -208,7 +208,7 @@ static void control_received(void) {
/* acknowledge packet recieved (clear valid) */
M66591_INTSTAT_MAIN &= ~(1<<3);
- usb_core_control_request(&temp);
+ usb_core_legacy_control_request(&temp);
}
/* This is a helper function, it is used to notife the stack that a transfer is
@@ -869,7 +869,7 @@ int usb_drv_send(int endpoint, void* ptr, int length)
/* This function begins a receive (on an OUT endpoint), it should not block
* so the actual receive is done in the interrupt handler.
*/
-int usb_drv_recv(int endpoint, void* ptr, int length)
+int usb_drv_recv_nonblocking(int endpoint, void* ptr, int length)
{
return mxx_queue(endpoint, ptr, length, false, false);
}
diff --git a/firmware/drivers/rds.c b/firmware/drivers/rds.c
index 0b9b227563..48c7d398a9 100644
--- a/firmware/drivers/rds.c
+++ b/firmware/drivers/rds.c
@@ -25,6 +25,8 @@
#include <kernel.h>
#include "rds.h"
#include "time.h"
+#include "timefuncs.h"
+#include "settings.h"
#include "string-extra.h"
#define TIMED_OUT(tick) \
@@ -72,15 +74,6 @@ static int rt_data_idx; /* rt_data[0 or 1] */
#define RT_DATA_INC(x) rt_data[rt_data_idx ^= (x)]
#endif /* (CONFIG_RDS & RDS_CFG_PROCESS) */
-#if (CONFIG_RDS & RDS_CFG_ISR)
-/* Functions are called in ISR context */
-#define rds_disable_irq_save() disable_irq_save()
-#define rds_restore_irq(old) restore_irq(old)
-#else /* !(CONFIG_RDS & RDS_CFG_ISR) */
-#define rds_disable_irq_save() 0
-#define rds_restore_irq(old) ((void)(old))
-#endif /* (CONFIG_RDS & RDS_CFG_ISR) */
-
/* RDS code table G0 to UTF-8 translation */
static const uint16_t rds_tbl_g0[0x100-0x20] =
{
@@ -195,8 +188,6 @@ static void register_activity(void)
/* resets the rds parser */
void rds_reset(void)
{
- int oldlevel = rds_disable_irq_save();
-
/* reset general info */
pi_code = 0;
ct_data = 0;
@@ -210,8 +201,6 @@ void rds_reset(void)
ps_segment = 0;
rt_segment = 0;
#endif /* (CONFIG_RDS & RDS_CFG_PROCESS) */
-
- rds_restore_irq(oldlevel);
}
/* initialises the rds parser */
@@ -223,8 +212,6 @@ void rds_init(void)
/* sync RDS state */
void rds_sync(void)
{
- int oldlevel = rds_disable_irq_save();
-
if (rds_active) {
if (TIMED_OUT(rds_timeout)) {
rds_reset();
@@ -238,8 +225,18 @@ void rds_sync(void)
}
}
}
+}
- rds_restore_irq(oldlevel);
+static void rds_set_time(time_t time)
+{
+ ct_data = time;
+#ifdef CONFIG_RTC
+ if (ct_data && global_settings.sync_rds_time) {
+ struct tm *tm = gmtime(&ct_data);
+
+ set_time(tm);
+ }
+#endif
}
#if (CONFIG_RDS & RDS_CFG_PROCESS)
@@ -384,7 +381,7 @@ static void handle_group4a(const uint16_t data[4])
seconds += hour * 3600;
seconds += minute * 60;
seconds += ((offset_sig == 0) ? offset_abs : -offset_abs) * 1800;
- ct_data = seconds;
+ rds_set_time(seconds);
}
}
@@ -445,7 +442,7 @@ void rds_push_info(enum rds_info_id info_id, uintptr_t data, size_t size)
SET_TIMEOUT(rt_copy_tmo, TEXT_TIMEOUT);
break;
case RDS_INFO_CT:
- ct_data = (time_t)data;
+ rds_set_time((time_t)data);
break;
default:;
@@ -458,8 +455,6 @@ void rds_push_info(enum rds_info_id info_id, uintptr_t data, size_t size)
/* read fully-processed RDS data */
size_t rds_pull_info(enum rds_info_id info_id, uintptr_t data, size_t size)
{
- int oldlevel = rds_disable_irq_save();
-
rds_sync();
switch (info_id) {
@@ -490,7 +485,5 @@ size_t rds_pull_info(enum rds_info_id info_id, uintptr_t data, size_t size)
default:
size = 0;
}
-
- rds_restore_irq(oldlevel);
return size;
}
diff --git a/firmware/drivers/rtc/rtc_d2.c b/firmware/drivers/rtc/rtc_d2.c
index 1d202410e2..726564a255 100644
--- a/firmware/drivers/rtc/rtc_d2.c
+++ b/firmware/drivers/rtc/rtc_d2.c
@@ -53,9 +53,9 @@ int rtc_read_datetime(struct tm *tm)
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);
+ set_day_of_year(tm);
return rc;
}
diff --git a/firmware/drivers/rtc/rtc_ds1339_ds3231.c b/firmware/drivers/rtc/rtc_ds1339_ds3231.c
index 8f2a531e5f..b77e059595 100644
--- a/firmware/drivers/rtc/rtc_ds1339_ds3231.c
+++ b/firmware/drivers/rtc/rtc_ds1339_ds3231.c
@@ -126,9 +126,9 @@ int rtc_read_datetime(struct tm *tm)
tm->tm_mday = BCD2DEC(buf[4] & 0x3f);
tm->tm_mon = BCD2DEC(buf[5] & 0x1f) - 1;
tm->tm_year = BCD2DEC(buf[6]) + 100;
- tm->tm_yday = 0; /* Not implemented for now */
set_day_of_week(tm);
+ set_day_of_year(tm);
return rc;
}
diff --git a/firmware/drivers/rtc/rtc_e8564.c b/firmware/drivers/rtc/rtc_e8564.c
index 4bb61410db..775ff86728 100644
--- a/firmware/drivers/rtc/rtc_e8564.c
+++ b/firmware/drivers/rtc/rtc_e8564.c
@@ -86,9 +86,9 @@ int rtc_read_datetime(struct tm *tm)
tm->tm_mday = BCD2DEC(buf[3] & 0x3f);
tm->tm_mon = BCD2DEC(buf[5] & 0x1f) - 1;
tm->tm_year = BCD2DEC(buf[6]) + 100;
- tm->tm_yday = 0; /* Not implemented for now */
set_day_of_week(tm);
+ set_day_of_year(tm);
return read;
}
diff --git a/firmware/drivers/rtc/rtc_mr100.c b/firmware/drivers/rtc/rtc_mr100.c
index 6e1b0b5f40..2f44137e38 100644
--- a/firmware/drivers/rtc/rtc_mr100.c
+++ b/firmware/drivers/rtc/rtc_mr100.c
@@ -144,9 +144,9 @@ int rtc_read_datetime(struct tm *tm)
tm->tm_mday = buf[2];
tm->tm_mon = buf[1] - 1;
tm->tm_year = buf[0] + 100;
- tm->tm_yday = 0; /* Not implemented for now */
set_day_of_week(tm);
+ set_day_of_year(tm);
return rc;
}
diff --git a/firmware/drivers/rtc/rtc_pcf50605.c b/firmware/drivers/rtc/rtc_pcf50605.c
index daa35fb13f..4d2560284c 100644
--- a/firmware/drivers/rtc/rtc_pcf50605.c
+++ b/firmware/drivers/rtc/rtc_pcf50605.c
@@ -52,9 +52,9 @@ int rtc_read_datetime(struct tm *tm)
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);
+ set_day_of_year(tm);
return rc;
}
@@ -84,7 +84,7 @@ int rtc_write_datetime(const struct tm *tm)
* Checks the PCF interrupt 1 register bit 7 to see if an alarm interrupt has
* triggered since last we checked.
*/
-bool rtc_check_alarm_flag(void)
+bool rtc_check_alarm_flag(void)
{
return pcf50605_read(0x02) & 0x80;
}
@@ -125,9 +125,9 @@ bool rtc_check_alarm_started(bool release_alarm)
static bool run_before = false, alarm_state;
bool rc;
- if (run_before) {
+ if (run_before) {
rc = alarm_state;
- alarm_state &= ~release_alarm;
+ alarm_state &= !release_alarm;
} else {
char rt[3], at[3];
/* The Ipod bootloader seems to read (and thus clear) the PCF interrupt
@@ -165,4 +165,3 @@ void rtc_get_alarm(int *h, int *m)
*m = BCD2DEC(buf[0]);
*h = BCD2DEC(buf[1]);
}
-
diff --git a/firmware/drivers/rtc/rtc_pcf50606.c b/firmware/drivers/rtc/rtc_pcf50606.c
index 540ebfff06..16ae5a3e9c 100644
--- a/firmware/drivers/rtc/rtc_pcf50606.c
+++ b/firmware/drivers/rtc/rtc_pcf50606.c
@@ -56,7 +56,7 @@ int rtc_read_datetime(struct tm *tm)
tm->tm_hour = buf[2];
tm->tm_mday = buf[4];
tm->tm_mon = buf[5] - 1;
- tm->tm_yday = 0; /* Not implemented for now */
+
#ifdef IRIVER_H300_SERIES
/* Special kludge to coexist with the iriver firmware. The iriver firmware
stores the date as 1965+nn, and allows a range of 1980..2064. We use
@@ -68,6 +68,7 @@ int rtc_read_datetime(struct tm *tm)
#endif /* IRIVER_H300_SERIES */
set_day_of_week(tm);
+ set_day_of_year(tm);
return rc;
}
diff --git a/firmware/drivers/rtc/rtc_rx5x348ab.c b/firmware/drivers/rtc/rtc_rx5x348ab.c
index 6d7b78b281..d95186e8be 100644
--- a/firmware/drivers/rtc/rtc_rx5x348ab.c
+++ b/firmware/drivers/rtc/rtc_rx5x348ab.c
@@ -52,9 +52,9 @@ int rtc_read_datetime(struct tm *tm)
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);
+ set_day_of_year(tm);
return 1;
}
diff --git a/firmware/drivers/rtc/rtc_s35380a.c b/firmware/drivers/rtc/rtc_s35380a.c
index f32c431990..6deaed6354 100644
--- a/firmware/drivers/rtc/rtc_s35380a.c
+++ b/firmware/drivers/rtc/rtc_s35380a.c
@@ -192,9 +192,9 @@ int rtc_read_datetime(struct tm *tm)
tm->tm_mday = buf[TIME_DAY];
tm->tm_mon = buf[TIME_MONTH] - 1;
tm->tm_year = buf[TIME_YEAR] + 100;
- tm->tm_yday = 0; /* Not implemented for now */
set_day_of_week(tm);
+ set_day_of_year(tm);
return ret;
}
diff --git a/firmware/drivers/rtc/rtc_s35390a.c b/firmware/drivers/rtc/rtc_s35390a.c
index b82029a114..6e69073254 100644
--- a/firmware/drivers/rtc/rtc_s35390a.c
+++ b/firmware/drivers/rtc/rtc_s35390a.c
@@ -79,9 +79,9 @@ int rtc_read_datetime(struct tm *tm)
tm->tm_mday = buf[2];
tm->tm_mon = buf[1] - 1;
tm->tm_year = buf[0] + 100;
- tm->tm_yday = 0; /* Not implemented for now */
set_day_of_week(tm);
+ set_day_of_year(tm);
return ret;
}
diff --git a/firmware/drivers/rtc/rtc_s3c2440.c b/firmware/drivers/rtc/rtc_s3c2440.c
index 6cd34f0e23..71f99ac6f4 100644
--- a/firmware/drivers/rtc/rtc_s3c2440.c
+++ b/firmware/drivers/rtc/rtc_s3c2440.c
@@ -39,9 +39,9 @@ int rtc_read_datetime(struct tm *tm)
tm->tm_mday = BCD2DEC(BCDDATE);
tm->tm_mon = BCD2DEC(BCDMON) - 1;
tm->tm_year = BCD2DEC(BCDYEAR) + 100;
- tm->tm_yday = 0; /* Not implemented for now */
set_day_of_week(tm);
+ set_day_of_year(tm);
return 1;
}
diff --git a/firmware/drivers/rtc/rtc_tcc77x.c b/firmware/drivers/rtc/rtc_tcc77x.c
deleted file mode 100644
index a85c93d65a..0000000000
--- a/firmware/drivers/rtc/rtc_tcc77x.c
+++ /dev/null
@@ -1,81 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2008 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 "rtc.h"
-#include "system.h"
-#include <stdbool.h>
-
-void rtc_init(void)
-{
-}
-
-int rtc_read_datetime(struct tm *tm)
-{
- (void)tm;
- return 0;
-}
-
-int rtc_write_datetime(const struct tm *tm)
-{
- (void)tm;
- return 1;
-}
-
-#ifdef HAVE_RTC_ALARM
-/**
- * Checks to see if an alarm interrupt has triggered since last we checked.
- */
-bool rtc_check_alarm_flag(void)
-{
-}
-
-/**
- * Enables or disables the alarm.
- */
-void rtc_enable_alarm(bool enable)
-{
-}
-
-/**
- * Check if alarm caused unit to start.
- */
-bool rtc_check_alarm_started(bool release_alarm)
-{
-}
-
-void rtc_set_alarm(int h, int m)
-{
- /* Convert to BCD */
-// pcf50605_write(0x12, ((m/10) << 4) | m%10);
-// pcf50605_write(0x13, ((h/10) << 4) | h%10);
-}
-
-void rtc_get_alarm(int *h, int *m)
-{
- char buf[2];
-
- /* Convert from BCD */
-// *m = ((buf[0] >> 4) & 0x7)*10 + (buf[0] & 0x0f);
-// *h = ((buf[1] >> 4) & 0x3)*10 + (buf[1] & 0x0f);
-}
-#endif /* HAVE_RTC_ALARM */
-
-
diff --git a/firmware/drivers/synaptics-mep.c b/firmware/drivers/synaptics-mep.c
index 01845bfe0c..bae17e08c7 100644
--- a/firmware/drivers/synaptics-mep.c
+++ b/firmware/drivers/synaptics-mep.c
@@ -560,7 +560,10 @@ int touchpad_read_device(char *data, int len)
{
/* for HDD6330 an absolute packet will follow for sensor nr 0 which we ignore */
#if defined(PHILIPS_HDD6330)
- if ((data[3]>>6) == 0) syn_read(tmp, 4);
+ if ((data[3]>>6) == 0)
+ syn_read(tmp, 4);
+ else
+ tmp[1] = 0x0; /* Initialize explicitly */
// relay tap gesture packet
if (tmp[1]==0x02) { data[1]=0x02; data[2]=0x00; data[3]=0x00; }
#endif
diff --git a/firmware/drivers/tuner/lv24020lp.c b/firmware/drivers/tuner/lv24020lp.c
index bb137d60ff..2fbf88b01b 100644
--- a/firmware/drivers/tuner/lv24020lp.c
+++ b/firmware/drivers/tuner/lv24020lp.c
@@ -80,16 +80,6 @@ static int fd_log = -1;
#define FM_CLOCK_PIN 4
#define FM_DATA_PIN 5
-#elif defined(IAUDIO_7)
-#define TUNER_GPIO_INPUT_VAL GPIOA
-#define TUNER_GPIO_OUTPUT_EN_SET(mask) (GPIOA_DIR |= (mask))
-#define TUNER_GPIO_OUTPUT_EN_CLEAR(mask) (GPIOA_DIR &= ~(mask))
-#define TUNER_GPIO_OUTPUT_VAL_SET(mask) (GPIOA |= (mask))
-#define TUNER_GPIO_OUTPUT_VAL_CLEAR(mask) (GPIOA &= ~(mask))
-#define FM_CLOCK_PIN 5
-#define FM_DATA_PIN 6
-#define FM_NRW_PIN 7
-
#elif defined(COWON_D2)
#define TUNER_GPIO_INPUT_VAL GPIOC
#define TUNER_GPIO_OUTPUT_EN_SET(mask) (GPIOC_DIR |= (mask))
diff --git a/firmware/drivers/tuner/si4700.c b/firmware/drivers/tuner/si4700.c
index 88ff6c69f7..8b4d0a1b19 100644
--- a/firmware/drivers/tuner/si4700.c
+++ b/firmware/drivers/tuner/si4700.c
@@ -553,32 +553,6 @@ void si4700_dbg_info(struct si4700_dbg_info *nfo)
}
#ifdef HAVE_RDS_CAP
-
-#if (CONFIG_RDS & RDS_CFG_ISR)
-static unsigned char isr_regbuf[(RDSD - STATUSRSSI + 1) * 2];
-
-/* Called by RDS interrupt on target */
-void si4700_rds_interrupt(void)
-{
- si4700_rds_read_raw_async(isr_regbuf, sizeof (isr_regbuf));
-}
-
-/* Handle RDS event from ISR */
-void si4700_rds_process(void)
-{
- uint16_t rds_data[4];
- int index = (RDSA - STATUSRSSI) * 2;
-
- for (int i = 0; i < 4; i++) {
- rds_data[i] = isr_regbuf[index] << 8 | isr_regbuf[index + 1];
- index += 2;
- }
-
- rds_process(rds_data);
-}
-
-#else /* !(CONFIG_RDS & RDS_CFG_ISR) */
-
/* Handle RDS event from thread */
void si4700_rds_process(void)
{
@@ -586,13 +560,67 @@ void si4700_rds_process(void)
if (tuner_powered())
{
- si4700_read_reg(RDSD);
+ si4700_read(6);
+#if (CONFIG_RDS & RDS_CFG_POLL)
+ /* we need to keep track of the ready bit because it stays set for 80ms
+ * and we must avoid processing it twice */
+
+ static bool old_rdsr = false;
+ bool rdsr = (cache[STATUSRSSI] & STATUSRSSI_RDSR);
+ if (rdsr && !old_rdsr)
+ rds_process(&cache[RDSA]);
+ old_rdsr = rdsr;
+#else
rds_process(&cache[RDSA]);
+#endif /* !(CONFIG_RDS & RDS_CFG_POLL) */
}
mutex_unlock(&fmr_mutex);
}
-#endif /* (CONFIG_RDS & RDS_CFG_ISR) */
+
+#if (CONFIG_RDS & RDS_CFG_POLL)
+static struct event_queue rds_queue;
+static uint32_t rds_stack[DEFAULT_STACK_SIZE / sizeof(uint32_t)];
+
+enum {
+ Q_POWERUP,
+};
+
+static void NORETURN_ATTR rds_thread(void)
+{
+ /* start up frozen */
+ int timeout = TIMEOUT_BLOCK;
+ struct queue_event ev;
+
+ while (true) {
+ queue_wait_w_tmo(&rds_queue, &ev, timeout);
+ switch (ev.id) {
+ case Q_POWERUP:
+ /* power up: timeout after 1 tick, else block indefinitely */
+ timeout = ev.data ? CONFIG_RDS_POLL_TICKS : TIMEOUT_BLOCK;
+ break;
+ case SYS_TIMEOUT:
+ /* Captures RDS data and processes it */
+ si4700_rds_process();
+ break;
+ }
+ }
+}
+
+/* true after full radio power up, and false before powering down */
+void si4700_rds_powerup(bool on)
+{
+ queue_post(&rds_queue, Q_POWERUP, on);
+}
+
+/* One-time RDS init at startup */
+void si4700_rds_init(void)
+{
+ queue_init(&rds_queue, false);
+ create_thread(rds_thread, rds_stack, sizeof(rds_stack), 0, "rds"
+ IF_PRIO(, PRIORITY_PLAYBACK) IF_COP(, CPU));
+}
+#endif /* !(CONFIG_RDS & RDS_CFG_POLL) */
#endif /* HAVE_RDS_CAP */
diff --git a/firmware/target/arm/usb-designware.c b/firmware/drivers/usb-designware.c
index 24c6055434..862445f2e8 100644
--- a/firmware/target/arm/usb-designware.c
+++ b/firmware/drivers/usb-designware.c
@@ -53,6 +53,33 @@
#define COMMIT_DCACHE_RANGE(b,s) commit_dcache_range(b,s)
#endif
+/* USB_DW_PHYSADDR(x) converts the address of buffer x to one usable with DMA.
+ * For example, converting a virtual address to a physical address.
+ *
+ * USB_DW_UNCACHEDADDR(x) is used to get an uncached pointer to a buffer.
+ * If the platform doesn't support this, define NO_UNCACHED_ADDR instead.
+ *
+ * Define POST_DMA_FLUSH if the driver should discard DMA RX buffers after a
+ * transfer completes. Needed if the CPU can speculatively fetch cache lines
+ * in any way, eg. due to speculative execution / prefetching.
+ */
+#if CONFIG_CPU == X1000
+# define USB_DW_PHYSADDR(x) PHYSADDR(x)
+# define USB_DW_UNCACHEDADDR(x) ((typeof(x))UNCACHEDADDR(x))
+# define POST_DMA_FLUSH
+#elif CONFIG_CPU == AS3525v2
+# define USB_DW_PHYSADDR(x) AS3525_PHYSICAL_ADDR(x)
+# define USB_DW_UNCACHEDADDR(x) AS3525_UNCACHED_ADDR(x)
+#elif CONFIG_CPU == S5L8701
+# define USB_DW_PHYSADDR(x) x
+# define NO_UNCACHED_ADDR /* Not known how to form uncached addresses */
+#elif CONFIG_CPU == S5L8702
+# define USB_DW_PHYSADDR(x) S5L8702_PHYSICAL_ADDR(x)
+# define USB_DW_UNCACHEDADDR(x) S5L8702_UNCACHED_ADDR(x)
+#elif !defined(USB_DW_ARCH_SLAVE)
+# error "Must define USB_DW_PHYSADDR / USB_DW_UNCACHEDADDR!"
+#endif
+
#ifndef USB_DW_TOUTCAL
#define USB_DW_TOUTCAL 0
#endif
@@ -68,19 +95,35 @@ enum usb_dw_epdir
USB_DW_EPDIR_OUT = 1,
};
-union usb_ep0_buffer
+enum usb_dw_ep0_state
{
- struct usb_ctrlrequest setup;
- uint8_t raw[64];
+ /* Waiting for a setup packet to arrive. This is the default state. */
+ EP0_SETUP,
+
+ /* Request wait states -- after submitting a request, we enter EP0_REQ
+ * (or EP0_REQ_CTRLWRITE for control writes). EP0_REQ is also used for
+ * the 2nd phase of a control write. EP0_REQ_CANCELLED is entered if we
+ * receive a setup packet before getting a response from the USB stack. */
+ EP0_REQ,
+ EP0_REQ_CTRLWRITE,
+ EP0_REQ_CANCELLED,
+
+ /* Waiting for a data phase to complete. */
+ EP0_DATA_IN,
+ EP0_DATA_OUT,
+
+ /* Waiting for the status phase */
+ EP0_STATUS_IN,
+ EP0_STATUS_OUT,
+
+ EP0_NUM_STATES
};
-static union usb_ep0_buffer ep0_buffer USB_DEVBSS_ATTR;
-
/* Internal EP state/info */
struct usb_dw_ep
{
struct semaphore complete;
- uint32_t* req_addr;
+ void* req_addr;
uint32_t req_size;
uint32_t* addr;
uint32_t sizeleft;
@@ -90,7 +133,42 @@ struct usb_dw_ep
uint8_t busy;
};
+/* Additional state for EP0 */
+struct usb_dw_ep0
+{
+ enum usb_dw_ep0_state state;
+ struct usb_ctrlrequest active_req;
+ struct usb_ctrlrequest pending_req;
+};
+
+static const char* const dw_dir_str[USB_DW_NUM_DIRS] =
+{
+ [USB_DW_EPDIR_IN] = "IN",
+ [USB_DW_EPDIR_OUT] = "OUT",
+};
+
+static const char* const dw_state_str[EP0_NUM_STATES] =
+{
+ [EP0_SETUP] = "setup",
+ [EP0_REQ] = "req",
+ [EP0_REQ_CTRLWRITE] = "req_cw",
+ [EP0_DATA_IN] = "dat_in",
+ [EP0_DATA_OUT] = "dat_out",
+ [EP0_STATUS_IN] = "sts_in",
+ [EP0_STATUS_OUT] = "sts_out",
+};
+
+static const char* const dw_resp_str[3] =
+{
+ [USB_CONTROL_ACK] = "ACK",
+ [USB_CONTROL_RECEIVE] = "RECV",
+ [USB_CONTROL_STALL] = "STALL",
+};
+
static struct usb_dw_ep usb_dw_ep_list[USB_NUM_ENDPOINTS][USB_DW_NUM_DIRS];
+static struct usb_dw_ep0 ep0;
+uint8_t _ep0_buffer[64] USB_DEVBSS_ATTR __attribute__((aligned(32)));
+uint8_t* ep0_buffer; /* Uncached, unless NO_UNCACHED_ADDR is defined */
static uint32_t usb_endpoints; /* available EPs mask */
@@ -108,35 +186,30 @@ static uint32_t epmis_msk;
static uint32_t ep_periodic_msk;
#endif
-static const char *dw_dir_str[USB_DW_NUM_DIRS] =
-{
- [USB_DW_EPDIR_IN] = "IN",
- [USB_DW_EPDIR_OUT] = "OUT",
-};
-
-
static struct usb_dw_ep *usb_dw_get_ep(int epnum, enum usb_dw_epdir epdir)
{
return &usb_dw_ep_list[epnum][epdir];
}
-static int usb_dw_maxpktsize(int epnum, enum usb_dw_epdir epdir)
+static uint32_t usb_dw_maxpktsize(int epnum, enum usb_dw_epdir epdir)
{
return epnum ? DWC_EPCTL(epnum, epdir) & 0x3ff : 64;
}
-static int usb_dw_maxxfersize(int epnum, enum usb_dw_epdir epdir)
+static uint32_t usb_dw_maxxfersize(int epnum, enum usb_dw_epdir epdir)
{
- return epnum ? ALIGN_DOWN_P2(MIN(hw_maxbytes,
- hw_maxpackets*usb_dw_maxpktsize(epnum, epdir)), CACHEALIGN_BITS) : 64;
+ /* EP0 can only transfer one packet at a time. */
+ if(epnum == 0)
+ return 64;
+
+ uint32_t maxpktsize = usb_dw_maxpktsize(epnum, epdir);
+ return CACHEALIGN_DOWN(MIN(hw_maxbytes, hw_maxpackets * maxpktsize));
}
/* Calculate number of packets (if size == 0 an empty packet will be sent) */
-static int usb_dw_calc_packets(uint32_t size, uint32_t maxpktsize)
+static uint32_t usb_dw_calc_packets(uint32_t size, uint32_t maxpktsize)
{
- int packets = (size + maxpktsize - 1) / maxpktsize;
- if (!packets) packets = 1;
- return packets;
+ return MAX(1, (size + maxpktsize - 1) / maxpktsize);
}
static int usb_dw_get_stall(int epnum, enum usb_dw_epdir epdir)
@@ -175,8 +248,8 @@ static unsigned usb_dw_bytes_in_txfifo(int epnum, uint32_t *sentbytes)
uint32_t dieptsiz = DWC_DIEPTSIZ(epnum);
uint32_t packetsleft = (dieptsiz >> 19) & 0x3ff;
if (!packetsleft) return 0;
- int maxpktsize = usb_dw_maxpktsize(epnum, USB_DW_EPDIR_IN);
- int packets = usb_dw_calc_packets(size, maxpktsize);
+ uint32_t maxpktsize = usb_dw_maxpktsize(epnum, USB_DW_EPDIR_IN);
+ uint32_t packets = usb_dw_calc_packets(size, maxpktsize);
uint32_t bytesleft = dieptsiz & 0x7ffff;
uint32_t bytespushed = size - bytesleft;
uint32_t bytespulled = (packets - packetsleft) * maxpktsize;
@@ -191,7 +264,7 @@ static unsigned usb_dw_bytes_in_txfifo(int epnum, uint32_t *sentbytes)
static void usb_dw_handle_rxfifo(void)
{
uint32_t rxsts = DWC_GRXSTSP;
- int pktsts = (rxsts >> 17) & 0xf;
+ uint32_t pktsts = (rxsts >> 17) & 0xf;
switch (pktsts)
{
@@ -199,19 +272,31 @@ static void usb_dw_handle_rxfifo(void)
case PKTSTS_SETUPRX:
{
int ep = rxsts & 0xf;
- int words = (((rxsts >> 4) & 0x7ff) + 3) >> 2;
- struct usb_dw_ep* dw_ep = usb_dw_get_ep(ep, USB_DW_EPDIR_OUT);
- if (dw_ep->busy)
+ uint32_t words = (((rxsts >> 4) & 0x7ff) + 3) >> 2;
+
+ /* Annoyingly, we need to special-case EP0. */
+ if(ep == 0)
{
+ uint32_t* addr = (uint32_t*)ep0_buffer;
while (words--)
- *dw_ep->addr++ = DWC_DFIFO(0);
+ *addr++ = DWC_DFIFO(0);
}
else
{
- /* Discard data */
- while (words--)
- (void) DWC_DFIFO(0);
+ struct usb_dw_ep* dw_ep = usb_dw_get_ep(ep, USB_DW_EPDIR_OUT);
+ if (dw_ep->busy)
+ {
+ while (words--)
+ *dw_ep->addr++ = DWC_DFIFO(0);
+ }
+ else
+ {
+ /* Discard data */
+ while (words--)
+ (void) DWC_DFIFO(0);
+ }
}
+
break;
}
case PKTSTS_OUTDONE:
@@ -283,7 +368,7 @@ static void usb_dw_handle_dtxfifo(int epnum)
{
/* We push whole packets to read consistent info on DIEPTSIZ
(i.e. when FIFO size is not maxpktsize multiplo). */
- int maxpktwords = usb_dw_maxpktsize(epnum, USB_DW_EPDIR_IN) >> 2;
+ uint32_t maxpktwords = usb_dw_maxpktsize(epnum, USB_DW_EPDIR_IN) >> 2;
words = (fifospace / maxpktwords) * maxpktwords;
}
@@ -449,7 +534,7 @@ static void usb_dw_nptx_unqueue(int epnum)
dw_ep->addr -= (bytesinfifo + 3) >> 2;
#else
(void) bytesinfifo;
- DWC_DIEPDMA(ep) = (uint32_t)(dw_ep->addr) + sentbytes;
+ DWC_DIEPDMA(ep) = USB_DW_PHYSADDR((uint32_t)(dw_ep->addr) + sentbytes);
#endif
DWC_DIEPTSIZ(ep) = PKTCNT(packetsleft) | (dw_ep->size - sentbytes);
@@ -655,57 +740,72 @@ static void usb_dw_reset_endpoints(void)
#endif
}
-static void usb_dw_start_xfer(int epnum,
- enum usb_dw_epdir epdir, const void* buf, int size)
+static void usb_dw_epstart(int epnum, enum usb_dw_epdir epdir,
+ void* buf, uint32_t size)
{
if ((uint32_t)buf & ((epdir == USB_DW_EPDIR_IN) ? 3 : CACHEALIGN_SIZE-1))
logf("%s: %s%d %p unaligned", __func__, dw_dir_str[epdir], epnum, buf);
struct usb_dw_ep* dw_ep = usb_dw_get_ep(epnum, epdir);
+ uint32_t xfersize = MIN(size, usb_dw_maxxfersize(epnum, epdir));
- dw_ep->busy = true;
- dw_ep->status = -1;
+ dw_ep->addr = (uint32_t*)buf;
+ dw_ep->size = xfersize;
dw_ep->sizeleft = size;
- size = MIN(size, usb_dw_maxxfersize(epnum, epdir));
- dw_ep->size = size;
+ dw_ep->status = -1;
+ dw_ep->busy = true;
- int packets = usb_dw_calc_packets(size, usb_dw_maxpktsize(epnum, epdir));
- uint32_t eptsiz = PKTCNT(packets) | size;
- uint32_t nak;
+ if (epnum == 0 && epdir == USB_DW_EPDIR_OUT)
+ {
+ /* FIXME: there's an extremely rare race condition here.
+ *
+ * 1. Host sends a control write.
+ * 2. We process the request.
+ * 3. (time passes)
+ * 4. This function is called via USB_CONTROL_RECEIVE response.
+ * 5. Right before we set CNAK, host sends another control write.
+ *
+ * So we may unintentionally receive data from the second request.
+ * It's possible to detect this when we see a setup packet because
+ * EP0 OUT will be busy. In principle it should even be possible to
+ * handle the 2nd request correctly. Currently we don't attempt to
+ * detect or recover from this error.
+ */
+ DWC_DOEPCTL(0) |= CNAK;
+ return;
+ }
- /* Set up data source */
- dw_ep->addr = (uint32_t*)buf;
-#ifndef USB_DW_ARCH_SLAVE
- DWC_EPDMA(epnum, epdir) = (uint32_t)buf;
-#endif
+ uint32_t maxpktsize = usb_dw_maxpktsize(epnum, epdir);
+ uint32_t packets = usb_dw_calc_packets(xfersize, maxpktsize);
+ uint32_t eptsiz = PKTCNT(packets) | xfersize;
+ uint32_t nak = CNAK;
if (epdir == USB_DW_EPDIR_IN)
{
#ifndef USB_DW_ARCH_SLAVE
- COMMIT_DCACHE_RANGE(buf, size);
+ COMMIT_DCACHE_RANGE(buf, xfersize);
#endif
#ifdef USB_DW_SHARED_FIFO
eptsiz |= MCCNT((ep_periodic_msk >> epnum) & 1);
#endif
- nak = CNAK;
+
}
else
{
#ifndef USB_DW_ARCH_SLAVE
- DISCARD_DCACHE_RANGE(buf, size);
+ DISCARD_DCACHE_RANGE(buf, xfersize);
#endif
- eptsiz |= STUPCNT(!epnum);
- nak = epnum ? CNAK : SNAK;
}
+#ifndef USB_DW_ARCH_SLAVE
+ DWC_EPDMA(epnum, epdir) = USB_DW_PHYSADDR((uint32_t)buf);
+#endif
DWC_EPTSIZ(epnum, epdir) = eptsiz;
-
- /* Enable the endpoint */
DWC_EPCTL(epnum, epdir) |= EPENA | nak;
#ifdef USB_DW_ARCH_SLAVE
/* Enable interrupts to start pushing data into the FIFO */
- if ((epdir == USB_DW_EPDIR_IN) && size)
+ if ((epdir == USB_DW_EPDIR_IN) && dw_ep->size > 0)
#ifdef USB_DW_SHARED_FIFO
DWC_GINTMSK |= ((ep_periodic_msk & (1 << epnum)) ? PTXFE : NPTXFE);
#else
@@ -714,25 +814,31 @@ static void usb_dw_start_xfer(int epnum,
#endif
}
-static void usb_dw_ep0_wait_setup(void)
-{
- usb_dw_start_xfer(0, USB_DW_EPDIR_OUT, ep0_buffer.raw, 64);
-}
-
-static void usb_dw_handle_setup_received(void)
+static void usb_dw_transfer(int epnum, enum usb_dw_epdir epdir,
+ void* buf, uint32_t size)
{
- static struct usb_ctrlrequest usb_ctrlsetup;
-
- usb_dw_flush_endpoint(0, USB_DW_EPDIR_IN);
+ struct usb_dw_ep* dw_ep = usb_dw_get_ep(epnum, epdir);
- memcpy(&usb_ctrlsetup, ep0_buffer.raw, sizeof(usb_ctrlsetup));
+ if (!dw_ep->active)
+ logf("%s: %s%d inactive", __func__, dw_dir_str[epdir], epnum);
+ if (dw_ep->busy)
+ logf("%s: %s%d busy", __func__, dw_dir_str[epdir], epnum);
- if (((usb_ctrlsetup.bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE)
- && ((usb_ctrlsetup.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
- && (usb_ctrlsetup.bRequest == USB_REQ_SET_ADDRESS))
- usb_dw_set_address(usb_ctrlsetup.wValue);
+ dw_ep->req_addr = buf;
+ dw_ep->req_size = size;
+ usb_dw_epstart(epnum, epdir, buf, size);
+}
- usb_core_control_request(&usb_ctrlsetup);
+static void usb_dw_ep0_recv(void)
+{
+#ifndef USB_DW_ARCH_SLAVE
+#ifdef NO_UNCACHED_ADDR
+ DISCARD_DCACHE_RANGE(&_ep0_buffer[0], 64);
+#endif
+ DWC_DOEPDMA(0) = USB_DW_PHYSADDR((uint32_t)&_ep0_buffer[0]);
+#endif
+ DWC_DOEPTSIZ(0) = STUPCNT(1) | PKTCNT(1) | 64;
+ DWC_DOEPCTL(0) |= EPENA | SNAK;
}
static void usb_dw_abort_endpoint(int epnum, enum usb_dw_epdir epdir)
@@ -746,60 +852,239 @@ static void usb_dw_abort_endpoint(int epnum, enum usb_dw_epdir epdir)
}
}
+static void usb_dw_control_received(struct usb_ctrlrequest* req)
+{
+ logf("%s(%p) state=%s", __func__, req, dw_state_str[ep0.state]);
+ logf(" bRequestType=%02x bRequest=%02x", req->bRequestType, req->bRequest);
+ logf(" wValue=%04x wIndex=%u wLength=%u", req->wValue, req->wIndex, req->wLength);
+
+ switch(ep0.state) {
+ case EP0_REQ:
+ case EP0_REQ_CTRLWRITE:
+ case EP0_REQ_CANCELLED:
+ /* Save the request for later */
+ memcpy(&ep0.pending_req, req, sizeof(*req));
+ ep0.state = EP0_REQ_CANCELLED;
+ break;
+
+ case EP0_DATA_IN:
+ case EP0_STATUS_IN:
+ case EP0_DATA_OUT:
+ case EP0_STATUS_OUT:
+ usb_core_control_complete(-1);
+ /* fallthrough */
+
+ case EP0_SETUP:
+ /* Save the request */
+ memcpy(&ep0.active_req, req, sizeof(*req));
+ req = &ep0.active_req;
+
+ /* Check for a SET ADDRESS request, which we must handle here */
+ if ((req->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE &&
+ (req->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD &&
+ (req->bRequest == USB_REQ_SET_ADDRESS))
+ usb_dw_set_address(req->wValue);
+
+ /* Check for control writes */
+ if (req->wLength > 0 && !(req->bRequestType & USB_DIR_IN))
+ ep0.state = EP0_REQ_CTRLWRITE;
+ else
+ ep0.state = EP0_REQ;
+
+ usb_dw_flush_endpoint(0, USB_DW_EPDIR_IN);
+ usb_core_control_request(req, NULL);
+ break;
+
+ default:
+ panicf("%s: bad state=%s", __func__, ep0.state >= EP0_NUM_STATES ? "unk" : dw_state_str[ep0.state]);
+ }
+}
+
+/* note: must be called with IRQs disabled */
+static void usb_dw_control_response(enum usb_control_response resp,
+ void* data, int length)
+{
+ struct usb_ctrlrequest* req = &ep0.active_req;
+
+ switch(ep0.state) {
+ case EP0_REQ:
+ case EP0_REQ_CTRLWRITE:
+ switch(resp) {
+ case USB_CONTROL_ACK:
+ if(req->wLength > 0 && (req->bRequestType & USB_DIR_IN))
+ ep0.state = EP0_DATA_IN; /* control read */
+ else
+ ep0.state = EP0_STATUS_IN; /* non-data or write */
+
+ usb_dw_transfer(0, USB_DW_EPDIR_IN, data, length);
+ break;
+
+ case USB_CONTROL_RECEIVE:
+ if(ep0.state != EP0_REQ_CTRLWRITE)
+ panicf("%s: bad response", __func__);
+
+ ep0.state = EP0_DATA_OUT;
+ usb_dw_transfer(0, USB_DW_EPDIR_OUT, data, length);
+ break;
+
+ case USB_CONTROL_STALL:
+ if(ep0.state == EP0_REQ_CTRLWRITE)
+ usb_dw_set_stall(0, USB_DW_EPDIR_OUT, 1);
+ else
+ usb_dw_set_stall(0, USB_DW_EPDIR_IN, 1);
+
+ ep0.state = EP0_SETUP;
+ break;
+ }
+ break;
+
+ case EP0_REQ_CANCELLED:
+ /* Terminate the old request */
+ usb_core_control_complete(-3);
+
+ /* Submit the pending request */
+ ep0.state = EP0_SETUP;
+ usb_dw_control_received(&ep0.pending_req);
+ break;
+
+ default:
+ panicf("%s: bad state=%s", __func__, dw_state_str[ep0.state]);
+ }
+}
+
+static void usb_dw_ep0_xfer_complete(enum usb_dw_epdir epdir,
+ int status, int transferred)
+{
+ struct usb_dw_ep* dw_ep = usb_dw_get_ep(0, epdir);
+
+ switch((ep0.state << 1) | epdir)
+ {
+ case (EP0_DATA_IN << 1) | USB_DW_EPDIR_IN:
+ ep0.state = EP0_STATUS_OUT;
+ usb_dw_transfer(0, USB_DW_EPDIR_OUT, NULL, 0);
+ break;
+
+ case (EP0_DATA_OUT << 1) | USB_DW_EPDIR_OUT:
+ ep0.state = EP0_REQ;
+ usb_core_control_request(&ep0.active_req, dw_ep->req_addr);
+ break;
+
+ case (EP0_STATUS_IN << 1) | USB_DW_EPDIR_IN:
+ case (EP0_STATUS_OUT << 1) | USB_DW_EPDIR_OUT:
+ if(status != 0 || transferred != 0)
+ usb_core_control_complete(-2);
+ else
+ usb_core_control_complete(0);
+
+ ep0.state = EP0_SETUP;
+ break;
+
+ default:
+ panicf("%s: state=%s dir=%s", __func__,
+ dw_state_str[ep0.state], dw_dir_str[epdir]);
+ }
+}
+
static void usb_dw_handle_xfer_complete(int epnum, enum usb_dw_epdir epdir)
{
struct usb_dw_ep* dw_ep = usb_dw_get_ep(epnum, epdir);
+ bool is_ep0out = (epnum == 0 && epdir == USB_DW_EPDIR_OUT);
if (!dw_ep->busy)
+ {
+ if(is_ep0out)
+ usb_dw_ep0_recv();
return;
+ }
- uint32_t bytesleft = DWC_EPTSIZ(epnum, epdir) & 0x7ffff;
+ uint32_t bytes_left = DWC_EPTSIZ(epnum, epdir) & 0x7ffff;
+ uint32_t transferred = (is_ep0out ? 64 : dw_ep->size) - bytes_left;
- if (!epnum && (epdir == USB_DW_EPDIR_OUT)) /* OUT0 */
+ if(transferred > dw_ep->sizeleft)
{
- int recvbytes = 64 - bytesleft;
- dw_ep->sizeleft = dw_ep->req_size - recvbytes;
- if (dw_ep->req_addr)
- memcpy(dw_ep->req_addr, ep0_buffer.raw, dw_ep->req_size);
+ /* Host sent more data than expected.
+ * Shouldn't happen for IN endpoints. */
+ dw_ep->status = -2;
+ goto complete;
}
- else
+
+ if(is_ep0out)
+ {
+#if defined(NO_UNCACHED_ADDR) && defined(POST_DMA_FLUSH)
+ DISCARD_DCACHE_RANGE(ep0_buffer, 64);
+#endif
+ memcpy(dw_ep->addr, ep0_buffer, transferred);
+ usb_dw_ep0_recv();
+ }
+
+ dw_ep->sizeleft -= transferred;
+
+ /* Start a new transfer if there is still more to go */
+ if(bytes_left == 0 && dw_ep->sizeleft > 0)
{
- dw_ep->sizeleft -= (dw_ep->size - bytesleft);
- if (!bytesleft && dw_ep->sizeleft)
- {
#ifndef USB_DW_ARCH_SLAVE
- dw_ep->addr += (dw_ep->size >> 2); /* words */
+ dw_ep->addr += (dw_ep->size >> 2); /* offset in words */
#endif
- usb_dw_start_xfer(epnum, epdir, dw_ep->addr, dw_ep->sizeleft);
- return;
- }
+ usb_dw_epstart(epnum, epdir, dw_ep->addr, dw_ep->sizeleft);
+ return;
+ }
- if (epdir == USB_DW_EPDIR_IN)
- {
- /* SNAK the disabled EP, otherwise IN tokens for this
- EP could raise unwanted EPMIS interrupts. Useful for
- usbserial when there is no data to send. */
- DWC_DIEPCTL(epnum) |= SNAK;
+ if(epdir == USB_DW_EPDIR_IN)
+ {
+ /* SNAK the disabled EP, otherwise IN tokens for this
+ EP could raise unwanted EPMIS interrupts. Useful for
+ usbserial when there is no data to send. */
+ DWC_DIEPCTL(epnum) |= SNAK;
#ifdef USB_DW_SHARED_FIFO
- /* See usb-s5l8701.c */
- if (usb_dw_config.use_ptxfifo_as_plain_buffer)
- {
- int dtxfnum = GET_DTXFNUM(epnum);
- if (dtxfnum)
- usb_dw_flush_fifo(TXFFLSH, dtxfnum);
- }
-#endif
+ /* See usb-s5l8701.c */
+ if (usb_dw_config.use_ptxfifo_as_plain_buffer)
+ {
+ int dtxfnum = GET_DTXFNUM(epnum);
+ if (dtxfnum)
+ usb_dw_flush_fifo(TXFFLSH, dtxfnum);
}
+#endif
+ }
+ else
+ {
+#if !defined(USB_DW_ARCH_SLAVE) && defined(POST_DMA_FLUSH)
+ /* On EP0 OUT we do not DMA into the request buffer,
+ * so do not discard the cache in this case. */
+ if(!is_ep0out)
+ DISCARD_DCACHE_RANGE(dw_ep->req_addr, dw_ep->req_size);
+#endif
}
- dw_ep->busy = false;
dw_ep->status = 0;
+
+ complete:
+ dw_ep->busy = false;
semaphore_release(&dw_ep->complete);
- int transfered = dw_ep->req_size - dw_ep->sizeleft;
- usb_core_transfer_complete(epnum, (epdir == USB_DW_EPDIR_OUT) ?
- USB_DIR_OUT : USB_DIR_IN, dw_ep->status, transfered);
+ int total_bytes = dw_ep->req_size - dw_ep->sizeleft;
+ if (epnum == 0)
+ {
+ usb_dw_ep0_xfer_complete(epdir, dw_ep->status, total_bytes);
+ }
+ else
+ {
+ usb_core_transfer_complete(epnum, (epdir == USB_DW_EPDIR_OUT) ?
+ USB_DIR_OUT : USB_DIR_IN, dw_ep->status, total_bytes);
+ }
+}
+
+static void usb_dw_handle_setup_received(void)
+{
+#if defined(NO_UNCACHED_ADDR) && defined(POST_DMA_FLUSH)
+ DISCARD_DCACHE_RANGE(ep0_buffer, 64);
+#endif
+ struct usb_ctrlrequest req;
+ memcpy(&req, ep0_buffer, sizeof(struct usb_ctrlrequest));
+
+ usb_dw_ep0_recv();
+
+ usb_dw_control_received(&req);
}
#ifdef USB_DW_SHARED_FIFO
@@ -813,7 +1098,7 @@ static int usb_dw_get_epmis(void)
/* Get the EP on the top of the queue, 0 < idx < number of available
IN endpoints */
- int idx = (gnptxsts >> 27) & 0xf;
+ uint32_t idx = (gnptxsts >> 27) & 0xf;
for (epmis = 0; epmis < USB_NUM_ENDPOINTS; epmis++)
if ((usb_endpoints & (1 << epmis)) && !idx--)
break;
@@ -951,6 +1236,7 @@ static void usb_dw_irq(void)
if (daint & (1 << (ep + 16)))
{
uint32_t epints = DWC_DOEPINT(ep);
+ DWC_DOEPINT(ep) = epints;
if (!ep)
{
@@ -958,17 +1244,31 @@ static void usb_dw_irq(void)
{
usb_dw_handle_setup_received();
}
- else if (epints & XFRC)
+
+ if (epints & XFRC)
{
- usb_dw_handle_xfer_complete(0, USB_DW_EPDIR_OUT);
+ if(epints & STATUSRECVD)
+ {
+ /* At the end of a control write's data phase, the
+ * controller writes a spurious OUTDONE token to the
+ * FIFO and raises StatusRecvd | XferCompl.
+ *
+ * We do not need or want this -- we've already handled
+ * the data phase by this point -- but EP0 is stoppped
+ * as a side effect of XferCompl, so we need to restart
+ * it to keep receiving packets. */
+ usb_dw_ep0_recv();
+ }
+ else if(!(epints & SETUPRECVD))
+ {
+ /* Only call this for normal data packets. Setup
+ * packets use the STUP interrupt handler instead. */
+ usb_dw_handle_xfer_complete(0, USB_DW_EPDIR_OUT);
+ }
}
- usb_dw_ep0_wait_setup();
- /* Clear interrupt after the current EP0 packet is handled */
- DWC_DOEPINT(0) = epints;
}
else
{
- DWC_DOEPINT(ep) = epints;
if (epints & XFRC)
{
usb_dw_handle_xfer_complete(ep, USB_DW_EPDIR_OUT);
@@ -982,14 +1282,14 @@ static void usb_dw_irq(void)
DWC_GINTSTS = USBRST;
usb_dw_set_address(0);
usb_dw_reset_endpoints();
- usb_dw_ep0_wait_setup();
usb_core_bus_reset();
}
if (DWC_GINTSTS & ENUMDNE)
{
DWC_GINTSTS = ENUMDNE;
- /* Nothing to do? */
+ ep0.state = EP0_SETUP;
+ usb_dw_ep0_recv();
}
}
@@ -1074,9 +1374,17 @@ static void usb_dw_init(void)
if (!initialized)
{
+#if !defined(USB_DW_ARCH_SLAVE) && !defined(NO_UNCACHED_ADDR)
+ ep0_buffer = USB_DW_UNCACHEDADDR(&_ep0_buffer[0]);
+#else
+ /* DMA is not used so we can operate on cached addresses */
+ ep0_buffer = &_ep0_buffer[0];
+#endif
+
for (int ep = 0; ep < USB_NUM_ENDPOINTS; ep++)
for (int dir = 0; dir < USB_DW_NUM_DIRS; dir++)
semaphore_init(&usb_dw_get_ep(ep, dir)->complete, 1, 0);
+
initialized = true;
}
@@ -1324,39 +1632,18 @@ void usb_drv_release_endpoint(int endpoint)
usb_dw_target_enable_irq();
}
-int usb_drv_recv(int endpoint, void* ptr, int length)
+int usb_drv_recv_nonblocking(int endpoint, void* ptr, int length)
{
- int epnum = EP_NUM(endpoint);
- struct usb_dw_ep* dw_ep = usb_dw_get_ep(epnum, USB_DW_EPDIR_OUT);
-
usb_dw_target_disable_irq();
- if (dw_ep->active)
- {
- dw_ep->req_addr = ptr;
- dw_ep->req_size = length;
- /* OUT0 is always launched waiting for SETUP packet,
- it is CNAKed to receive app data */
- if (epnum == 0)
- DWC_DOEPCTL(0) |= CNAK;
- else
- usb_dw_start_xfer(epnum, USB_DW_EPDIR_OUT, ptr, length);
- }
+ usb_dw_transfer(EP_NUM(endpoint), USB_DW_EPDIR_OUT, ptr, length);
usb_dw_target_enable_irq();
return 0;
}
int usb_drv_send_nonblocking(int endpoint, void *ptr, int length)
{
- int epnum = EP_NUM(endpoint);
- struct usb_dw_ep* dw_ep = usb_dw_get_ep(epnum, USB_DW_EPDIR_IN);
-
usb_dw_target_disable_irq();
- if (dw_ep->active)
- {
- dw_ep->req_addr = ptr;
- dw_ep->req_size = length;
- usb_dw_start_xfer(epnum, USB_DW_EPDIR_IN, ptr, length);
- }
+ usb_dw_transfer(EP_NUM(endpoint), USB_DW_EPDIR_IN, ptr, length);
usb_dw_target_enable_irq();
return 0;
}
@@ -1379,3 +1666,11 @@ int usb_drv_send(int endpoint, void *ptr, int length)
return dw_ep->status;
}
+
+void usb_drv_control_response(enum usb_control_response resp,
+ void* data, int length)
+{
+ usb_dw_target_disable_irq();
+ usb_dw_control_response(resp, data, length);
+ usb_dw_target_enable_irq();
+}
diff --git a/firmware/events.c b/firmware/events.c
index 8015442cc2..9b810c5d0d 100644
--- a/firmware/events.c
+++ b/firmware/events.c
@@ -28,70 +28,69 @@
struct sysevent {
unsigned short id;
bool oneshot;
- bool has_user_data;
union {
- void (*callback)(unsigned short id, void *event_data);
- struct {
- void (*callback2)(unsigned short id, void *event_data, void *user_data);
- void *user_data;
- };
+ void (*cb)(unsigned short id, void *event_data);
+ void (*cb_ex)(unsigned short id, void *event_data, void *user_data);
} handler;
+ void *user_data;
};
static struct sysevent events[MAX_SYS_EVENTS];
+static int invalid_userdata;
-static bool do_add_event(unsigned short id, bool oneshot, bool user_data_valid,
+static bool do_add_event(unsigned short id, bool oneshot,
void *handler, void *user_data)
{
- int i;
-
- /* Check if the event already exists. */
- for (i = 0; i < MAX_SYS_EVENTS; i++)
+ size_t free = MAX_SYS_EVENTS;
+ struct sysevent *ev;
+ /* Check if the event already exists. & lowest free slot available */
+ for (size_t i = MAX_SYS_EVENTS - 1; i < MAX_SYS_EVENTS; i--)
{
- if (events[i].handler.callback == handler && events[i].id == id
- && (!user_data_valid || (user_data == events[i].handler.user_data)))
+ ev = &events[i];
+ if (ev->handler.cb == NULL)
+ free = i;
+
+ if (ev->id == id && ev->handler.cb == handler && user_data == ev->user_data)
+ {
return false;
+ }
}
-
- /* Try to find a free slot. */
- for (i = 0; i < MAX_SYS_EVENTS; i++)
+
+ /* is there a free slot? */
+ if (free < MAX_SYS_EVENTS)
{
- if (events[i].handler.callback == NULL)
- {
- events[i].id = id;
- events[i].oneshot = oneshot;
- if ((events[i].has_user_data = user_data_valid))
- events[i].handler.user_data = user_data;
- events[i].handler.callback = handler;
- return true;
- }
+ ev = &events[free];
+ ev->id = id;
+ ev->handler.cb = handler;
+ ev->user_data = user_data;
+ ev->oneshot = oneshot;
+
+ return true;
}
-
+
panicf("event line full");
return false;
}
bool add_event(unsigned short id, void (*handler)(unsigned short id, void *data))
{
- return do_add_event(id, false, false, handler, NULL);
+ return do_add_event(id, false, handler, &invalid_userdata);
}
-bool add_event_ex(unsigned short id, bool oneshot, void (*handler)(unsigned short id, void *event_data, void *user_data), void *user_data)
+bool add_event_ex(unsigned short id, bool oneshot,
+ void (*handler)(unsigned short id, void *event_data, void *user_data), void *user_data)
{
- return do_add_event(id, oneshot, true, handler, user_data);
+ return do_add_event(id, oneshot, handler, user_data);
}
-static void do_remove_event(unsigned short id, bool user_data_valid,
- void *handler, void *user_data)
+static void do_remove_event(unsigned short id, void *handler, void *user_data)
{
- int i;
-
- for (i = 0; i < MAX_SYS_EVENTS; i++)
+ for (size_t i = 0; i < MAX_SYS_EVENTS; i++)
{
- if (events[i].id == id && events[i].handler.callback == handler
- && (!user_data_valid || (user_data == events[i].handler.user_data)))
+ struct sysevent *ev = &events[i];
+ if (ev->id == id && ev->handler.cb == handler && user_data == ev->user_data)
{
- events[i].handler.callback = NULL;
+ ev->handler.cb = NULL;
return;
}
}
@@ -99,31 +98,31 @@ static void do_remove_event(unsigned short id, bool user_data_valid,
void remove_event(unsigned short id, void (*handler)(unsigned short id, void *data))
{
- do_remove_event(id, false, handler, NULL);
+ do_remove_event(id, handler, &invalid_userdata);
}
void remove_event_ex(unsigned short id,
void (*handler)(unsigned short id, void *event_data, void *user_data),
void *user_data)
{
- do_remove_event(id, true, handler, user_data);
+ do_remove_event(id, handler, user_data);
}
void send_event(unsigned short id, void *data)
{
- int i;
-
- for (i = 0; i < MAX_SYS_EVENTS; i++)
+ for (size_t i = 0; i < MAX_SYS_EVENTS; i++)
{
- if (events[i].id == id && events[i].handler.callback != NULL)
+ struct sysevent *ev = &events[i];
+ if (ev->id == id && ev->handler.cb != NULL)
{
- if (events[i].has_user_data)
- events[i].handler.callback2(id, data, events[i].handler.user_data);
+ if (ev->user_data != &invalid_userdata)
+ {
+ ev->handler.cb_ex(id, data, ev->user_data);
+ if (ev->oneshot) /* only _ex events have option of oneshot */
+ ev->handler.cb = NULL;
+ }
else
- events[i].handler.callback(id, data);
-
- if (events[i].oneshot)
- events[i].handler.callback = NULL;
+ ev->handler.cb(id, data);
}
}
}
diff --git a/firmware/export/ak4376.h b/firmware/export/ak4376.h
index eb06755e92..ad842b2b80 100644
--- a/firmware/export/ak4376.h
+++ b/firmware/export/ak4376.h
@@ -22,7 +22,9 @@
#ifndef __AK4376_H__
#define __AK4376_H__
-#define AUDIOHW_CAPS (FILTER_ROLL_OFF_CAP|POWER_MODE_CAP)
+/* The target config must define this; defining it here would prevent
+ the target from supporting audio recording via an alternate codec. */
+/* #define AUDIOHW_CAPS (FILTER_ROLL_OFF_CAP|POWER_MODE_CAP) */
#define AUDIOHW_HAVE_SHORT2_ROLL_OFF
#define AK4376_MIN_VOLUME (-890)
@@ -104,10 +106,12 @@ AUDIOHW_SETTING(POWER_MODE, "", 0, 1, 0, 1, 0)
#define AK4376_FS_176 17
#define AK4376_FS_192 18
-/* Functions to power on / off the DAC which should be called from
- * the target's audiohw_init() / audiohw_close() implementation.
+/* Functions to power on / off the DAC.
+ *
+ * NOTE: Target must call ak4376_set_frequency() after ak4376_open() to
+ * finish the power-up sequence of the headphone amp.
*/
-extern void ak4376_init(void);
+extern void ak4376_open(void);
extern void ak4376_close(void);
/* Register read/write. Cached to avoid redundant reads/writes. */
@@ -117,16 +121,17 @@ extern int ak4376_read(int reg);
/* Target-specific function to set the PDN pin level. */
extern void ak4376_set_pdn_pin(int level);
-/* Target-specific function to control the external master clock frequency.
- * This is called by the ak4376's audiohw implementation when switching to
- * or from a frequency that is configured to use this clock source.
- *
- * - hw_freq is the new sample rate -- one of the HW_FREQ_XX constants.
- * - enabled is true if clock should be output, false if not.
+/* Set overall output volume */
+extern void ak4376_set_volume(int vol_l, int vol_r);
+
+/* Set the roll-off filter */
+extern void ak4376_set_filter_roll_off(int val);
+
+/* Set audio sampling frequency and power mode.
*
- * The return value is the master clock rate as a multiple of the sampling
- * frequency. The allowed multiples depend on the sampling frequency, shown
- * in the table below.
+ * If the I2S master clock is being supplied externally, the caller must also
+ * give the master clock multiplier 'mult'. The accepted values depend on the
+ * sampling rate, see below:
*
* +-----------+------------------------+
* | frequency | master clock rate |
@@ -137,16 +142,13 @@ extern void ak4376_set_pdn_pin(int level);
* | 128 - 192 | 128fs |
* +-----------+------------------------+
*
- * For example, at 48 KHz you could return either 256 or 512 depending on
- * the rate you decided to actually use.
- *
- * You need to return a valid master multiplier for supported frequencies
- * even when enabled = false, since the driver needs to know the multiplier
- * _before_ enabling the clock.
+ * Switching between high-power and low-power mode requires the same registers
+ * and power-up / power-down sequences as a frequency switch, so both settings
+ * are controlled by this function.
*
- * For unsupported frequencies you don't need to return a valid master
- * multiplier, because the DAC doesn't need the return value in such cases.
+ * high power mode -- use power_mode=SOUND_HIGH_POWER
+ * low power mode -- use power_mode=SOUND_LOW_POWER
*/
-extern int ak4376_set_mclk_freq(int hw_freq, bool enabled);
+extern void ak4376_set_freqmode(int fsel, int mult, int power_mode);
#endif /* __AK4376_H__ */
diff --git a/firmware/export/ata.h b/firmware/export/ata.h
index 7c5fd3a8d0..7c7c60e898 100644
--- a/firmware/export/ata.h
+++ b/firmware/export/ata.h
@@ -168,6 +168,9 @@ int ata_spinup_time(void); /* ticks */
/* Returns 1 if drive is solid-state */
int ata_disk_isssd(void);
+/* Returns 1 if the drive can be powered off safely */
+int ata_disk_can_poweroff(void);
+
#ifdef HAVE_ATA_DMA
/* Returns current DMA mode */
int ata_get_dma_mode(void);
diff --git a/firmware/export/audio.h b/firmware/export/audio.h
index c2c23dfd5c..4fa7841b65 100644
--- a/firmware/export/audio.h
+++ b/firmware/export/audio.h
@@ -154,7 +154,6 @@ void audio_record(const char *filename);
void audio_stop_recording(void);
void audio_pause_recording(void);
void audio_resume_recording(void);
-void audio_new_file(const char *filename);
void audio_set_recording_options(struct audio_recording_options *options);
void audio_set_recording_gain(int left, int right, int type);
unsigned long audio_recorded_time(void);
diff --git a/firmware/export/audiohw.h b/firmware/export/audiohw.h
index ceafc6ebf7..067118000e 100644
--- a/firmware/export/audiohw.h
+++ b/firmware/export/audiohw.h
@@ -192,6 +192,8 @@ struct sound_settings_info
#include "tsc2100.h"
#elif defined(HAVE_JZ4740_CODEC)
#include "jz4740-codec.h"
+#elif defined(HAVE_X1000_ICODEC_PLAY)
+#include "x1000-codec.h"
#elif defined(HAVE_AK4537)
#include "ak4537.h"
#elif defined(HAVE_AK4376)
@@ -210,12 +212,17 @@ struct sound_settings_info
#include "df1704.h"
#elif defined(HAVE_PCM1792_CODEC)
#include "pcm1792.h"
+#elif defined(HAVE_EROS_QN_CODEC)
+#include "eros_qn_codec.h"
+#include "es9018k2m.h"
#elif defined(HAVE_NWZ_LINUX_CODEC)
#include "nwzlinux_codec.h"
#elif defined(HAVE_CS4398)
#include "cs4398.h"
#elif defined(HAVE_ES9018)
#include "es9018.h"
+#elif defined(HAVE_ES9218)
+#include "es9218.h"
#elif (CONFIG_PLATFORM & (PLATFORM_ANDROID | PLATFORM_MAEMO \
| PLATFORM_PANDORA | PLATFORM_SDL))
#include "hosted_codec.h"
@@ -233,6 +240,11 @@ struct sound_settings_info
#include "erosqlinux_codec.h"
#endif
+#if defined(HAVE_X1000_ICODEC_REC) && !defined(HAVE_X1000_ICODEC_PLAY)
+/* Targets may have an external DAC above, but use icodec for recording only */
+#include "x1000-codec.h"
+#endif
+
/* convert caps into defines */
#ifdef AUDIOHW_CAPS
/* Tone controls */
@@ -594,6 +606,12 @@ void audiohw_set_filter_roll_off(int val);
#endif
#ifdef AUDIOHW_HAVE_POWER_MODE
+enum audiohw_power_mode
+{
+ SOUND_HIGH_POWER = 0,
+ SOUND_LOW_POWER,
+};
+
/**
* Set DAC's power saving mode.
* @param enable 0 - highest performance, 1 - battery saving
@@ -688,4 +706,9 @@ AUDIOHW_SETTING(BALANCE, "%", 0, 1, -100, 100, 0)
AUDIOHW_SETTING(CHANNELS, "", 0, 1, 0, 5, 0)
AUDIOHW_SETTING(STEREO_WIDTH, "%", 0, 5, 0, 250, 100)
+/* if not otherwise defined, set to 16 */
+#if !defined(PCM_NATIVE_BITDEPTH)
+# define PCM_NATIVE_BITDEPTH 16
+#endif
+
#endif /* _AUDIOHW_H_ */
diff --git a/firmware/export/axp-pmu.h b/firmware/export/axp-pmu.h
new file mode 100644
index 0000000000..24c992dea3
--- /dev/null
+++ b/firmware/export/axp-pmu.h
@@ -0,0 +1,149 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef __AXP_PMU_H__
+#define __AXP_PMU_H__
+
+#include "config.h"
+#include <stdbool.h>
+#include <stdint.h>
+
+/* ADC channels */
+#define ADC_ACIN_VOLTAGE 0
+#define ADC_ACIN_CURRENT 1
+#define ADC_VBUS_VOLTAGE 2
+#define ADC_VBUS_CURRENT 3
+#define ADC_INTERNAL_TEMP 4
+#define ADC_TS_INPUT 5
+#define ADC_BATTERY_VOLTAGE 6
+#define ADC_CHARGE_CURRENT 7
+#define ADC_DISCHARGE_CURRENT 8
+#define ADC_APS_VOLTAGE 9
+#define NUM_ADC_CHANNELS 10
+
+/* ADC sampling rates */
+#define AXP_ADC_RATE_25HZ 0
+#define AXP_ADC_RATE_50HZ 1
+#define AXP_ADC_RATE_100HZ 2
+#define AXP_ADC_RATE_200HZ 3
+
+/* Return values of axp_battery_status() */
+#define AXP_BATT_DISCHARGING 0
+#define AXP_BATT_CHARGING 1
+#define AXP_BATT_FULL 2
+
+/* Bits returned by axp_input_status() */
+#define AXP_INPUT_AC (1 << 0)
+#define AXP_INPUT_USB (1 << 1)
+#define AXP_INPUT_BATTERY (1 << 2)
+#define AXP_INPUT_EXTERNAL (AXP_INPUT_AC|AXP_INPUT_USB)
+
+/* Power supplies known by this driver. Not every chip has all supplies! */
+#define AXP_SUPPLY_DCDC1 0
+#define AXP_SUPPLY_DCDC2 1
+#define AXP_SUPPLY_DCDC3 2
+#define AXP_SUPPLY_LDO1 3
+#define AXP_SUPPLY_LDO2 4
+#define AXP_SUPPLY_LDO3 5
+#define AXP_SUPPLY_LDO_IO0 6
+#define AXP_NUM_SUPPLIES 7
+
+/* Special values returned by axp_supply_get_voltage */
+#define AXP_SUPPLY_NOT_PRESENT INT_MIN
+#define AXP_SUPPLY_DISABLED (-1)
+
+/* Registers -- common to AXP173 and AXP192 (incomplete listing) */
+#define AXP_REG_POWERSTATUS 0x00
+#define AXP_REG_CHARGESTATUS 0x01
+#define AXP_REG_CHIP_ID 0x03
+#define AXP_REG_PWROUTPUTCTRL1 0x10
+#define AXP_REG_PWROUTPUTCTRL2 0x12
+#define AXP_REG_SHUTDOWNLEDCTRL 0x32
+#define AXP_REG_CHARGECONTROL1 0x33
+#define AXP_REG_DCDCWORKINGMODE 0x80
+#define AXP_REG_ADCENABLE1 0x82
+#define AXP_REG_ADCENABLE2 0x83
+#define AXP_REG_ADCSAMPLERATE 0x84
+#define AXP_REG_COULOMBCOUNTERBASE 0xb0
+#define AXP_REG_COULOMBCOUNTERCTRL 0xb8
+
+/* AXP192-only registers (incomplete listing) */
+#define AXP192_REG_GPIO0FUNCTION 0x90
+#define AXP192_REG_GPIO1FUNCTION 0x92
+#define AXP192_REG_GPIO2FUNCTION 0x93
+#define AXP192_REG_GPIOSTATE1 0x94
+
+/* Must be called from power_init() to initialize the driver state */
+extern void axp_init(void);
+
+/* - axp_supply_set_voltage(): set a supply voltage to the given value
+ * in millivolts. Pass a voltage of AXP_SUPPLY_DISABLED to shut off
+ * the supply. Any invalid supply or voltage will make the call a no-op.
+ *
+ * - axp_supply_get_voltage() returns a supply voltage in millivolts.
+ * If the supply is powered off, returns AXP_SUPPLY_DISABLED.
+ * If the chip does not have the supply, returns AXP_SUPPLY_NOT_PRESENT.
+ */
+extern void axp_supply_set_voltage(int supply, int voltage);
+extern int axp_supply_get_voltage(int supply);
+
+/* Basic battery and power supply status */
+extern int axp_battery_status(void);
+extern int axp_input_status(void);
+
+/* ADC access -- ADCs which are not enabled will return INT_MIN if read.
+ * The output of axp_adc_read() is normalized to appropriate units:
+ *
+ * - for voltages, the scale is millivolts
+ * - for currents, the scale is milliamps
+ * - for temperatures, the scale is tenths of a degree Celsius
+ * - for power, the scale is microwatts
+ *
+ * See the comment in axp_adc_conv_raw() for raw value precision/scale.
+ */
+extern int axp_adc_read(int adc);
+extern int axp_adc_read_raw(int adc);
+extern int axp_adc_conv_raw(int adc, int value);
+extern void axp_adc_set_enabled(int adc_bits);
+extern int axp_adc_get_rate(void);
+extern void axp_adc_set_rate(int rate);
+
+/* - axp_cc_read() reads the coulomb counters
+ * - axp_cc_clear() resets both counters to zero
+ * - axp_cc_enable() will stop/start the counters running
+ * - axp_cc_is_enabled() returns true if the counters are running
+ */
+extern void axp_cc_read(uint32_t* charge, uint32_t* discharge);
+extern void axp_cc_clear(void);
+extern void axp_cc_enable(bool en);
+extern bool axp_cc_is_enabled(void);
+
+/* Set/get maximum charging current in milliamps */
+extern void axp_set_charge_current(int current_mA);
+extern int axp_get_charge_current(void);
+
+/* Set the shutdown bit */
+extern void axp_power_off(void);
+
+/* Debug menu */
+extern bool axp_debug_menu(void);
+
+#endif /* __AXP_PMU_H__ */
diff --git a/firmware/export/axp173.h b/firmware/export/axp173.h
deleted file mode 100644
index 60519138e1..0000000000
--- a/firmware/export/axp173.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2021 Aidan MacDonald
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ****************************************************************************/
-
-#ifndef __AXP173_H__
-#define __AXP173_H__
-
-#include <stdbool.h>
-#include <stdint.h>
-
-#define ADC_ACIN_VOLTAGE 0
-#define ADC_ACIN_CURRENT 1
-#define ADC_VBUS_VOLTAGE 2
-#define ADC_VBUS_CURRENT 3
-#define ADC_INTERNAL_TEMP 4
-#define ADC_TS_INPUT 5
-#define ADC_BATTERY_VOLTAGE 6
-#define ADC_CHARGE_CURRENT 7
-#define ADC_DISCHARGE_CURRENT 8
-#define ADC_APS_VOLTAGE 9
-#define ADC_BATTERY_POWER 10
-#define NUM_ADC_CHANNELS 11
-
-/* ADC sampling rates */
-#define AXP173_ADC_RATE_25HZ 0
-#define AXP173_ADC_RATE_50HZ 1
-#define AXP173_ADC_RATE_100HZ 2
-#define AXP173_ADC_RATE_200HZ 3
-
-/* Return values of axp173_battery_status() */
-#define AXP173_BATT_DISCHARGING 0
-#define AXP173_BATT_CHARGING 1
-#define AXP173_BATT_FULL 2
-
-/* Bits returned by axp173_input_status() */
-#define AXP173_INPUT_AC (1 << 0)
-#define AXP173_INPUT_USB (1 << 1)
-#define AXP173_INPUT_BATTERY (1 << 2)
-#define AXP173_INPUT_EXTERNAL (AXP173_INPUT_AC|AXP173_INPUT_USB)
-
-/* Must be called from power_init() to initialize the driver state */
-extern void axp173_init(void);
-
-/* Basic battery and power supply status */
-extern int axp173_battery_status(void);
-extern int axp173_input_status(void);
-
-/* ADC access -- ADCs which are not enabled will return INT_MIN if read.
- * The output of axp173_adc_read() is normalized to appropriate units:
- *
- * - for voltages, the scale is millivolts
- * - for currents, the scale is milliamps
- * - for temperatures, the scale is tenths of a degree Celsius
- * - for power, the scale is microwatts
- *
- * See the comment in axp173_adc_conv_raw() for raw value precision/scale.
- */
-extern int axp173_adc_read(int adc);
-extern int axp173_adc_read_raw(int adc);
-extern int axp173_adc_conv_raw(int adc, int value);
-extern int axp173_adc_get_enabled(void);
-extern void axp173_adc_set_enabled(int adc_bits);
-extern int axp173_adc_get_rate(void);
-extern void axp173_adc_set_rate(int rate);
-
-/* - axp173_cc_read() reads the coulomb counters
- * - axp173_cc_clear() resets both counters to zero
- * - axp173_cc_enable() will stop/start the counters running
- */
-extern void axp173_cc_read(uint32_t* charge, uint32_t* discharge);
-extern void axp173_cc_clear(void);
-extern void axp173_cc_enable(bool en);
-
-/* Debug menu */
-extern bool axp173_debug_menu(void);
-
-#endif /* __AXP173_H__ */
diff --git a/firmware/export/backlight.h b/firmware/export/backlight.h
index 6d029790b3..c94be264ca 100644
--- a/firmware/export/backlight.h
+++ b/firmware/export/backlight.h
@@ -53,8 +53,8 @@ void backlight_set_timeout_plugged(int value);
#ifdef HAS_BUTTON_HOLD
void backlight_hold_changed(bool hold_button);
-void backlight_set_on_button_hold(int index);
#endif
+void backlight_set_on_button_hold(int index);
#if defined(HAVE_LCD_SLEEP) && defined(HAVE_LCD_SLEEP_SETTING)
void lcd_set_sleep_after_backlight_off(int timeout_seconds);
@@ -98,16 +98,12 @@ extern int backlight_brightness;
void backlight_set_brightness(int val);
#endif /* HAVE_BACKLIGHT_BRIGHTNESS */
-#ifdef HAVE_BUTTONLIGHT_BRIGHTNESS
void buttonlight_set_brightness(int val);
-#endif /* HAVE_BUTTONLIGHT_BRIGHTNESS */
-#ifdef HAVE_BUTTON_LIGHT
void buttonlight_on_ignore(bool value, int timeout);
void buttonlight_on(void);
void buttonlight_off(void);
void buttonlight_set_timeout(int value);
-#endif
/* Private API for use in target tree backlight code only */
#ifdef HAVE_BUTTON_LIGHT
diff --git a/firmware/export/backtrace.h b/firmware/export/backtrace.h
index 283e293b2a..fb007ab004 100644
--- a/firmware/export/backtrace.h
+++ b/firmware/export/backtrace.h
@@ -25,6 +25,9 @@
#ifdef BACKTRACE_UNWARMINDER
#include "backtrace-unwarminder.h"
#endif
+#ifdef BACKTRACE_MIPSUNWINDER
+#include "backtrace-mipsunwinder.h"
+#endif
/* Print a backtrace using lcd_* functions, starting at the given line and updating
* the line number. On targets that support it (typically native targets), the
diff --git a/firmware/export/button.h b/firmware/export/button.h
index 1745947230..3abad2d4f0 100644
--- a/firmware/export/button.h
+++ b/firmware/export/button.h
@@ -107,7 +107,7 @@ int button_apply_acceleration(const unsigned int data);
BUTTON_MULTIMEDIA_FFWD)
#ifdef HAVE_TOUCHSCREEN
-int touchscreen_last_touch(void);
+long touchscreen_last_touch(void);
#if (!defined(BUTTON_TOPLEFT) || !defined(BUTTON_TOPMIDDLE) \
|| !defined(BUTTON_TOPRIGHT) || !defined(BUTTON_MIDLEFT) \
diff --git a/firmware/export/config.h b/firmware/export/config.h
index db1c589043..26ed7395ff 100644
--- a/firmware/export/config.h
+++ b/firmware/export/config.h
@@ -68,9 +68,6 @@
#define DSC25 25
#define DM320 320
#define IMX31L 31
-#define TCC770 770
-#define TCC771L 771
-#define TCC773L 773
#define TCC7801 7801
#define S5L8700 8700
#define S5L8701 8701
@@ -104,24 +101,19 @@
#define IPOD_4G_PAD 7
#define IPOD_3G_PAD 8
#define IPOD_1G2G_PAD 9
-#define IRIVER_IFP7XX_PAD 10
#define GIGABEAT_PAD 11
#define IRIVER_H10_PAD 12
#define SANSA_E200_PAD 13
#define SANSA_C200_PAD 14
-#define TATUNG_TPJ1022_PAD 15
#define MROBE100_PAD 17
#define MROBE500_PAD 18
#define GIGABEAT_S_PAD 19
-#define LOGIK_DAX_PAD 20
-#define IAUDIO67_PAD 21
#define COWON_D2_PAD 22
#define IAUDIO_M3_PAD 23
#define CREATIVEZVM_PAD 24
#define SANSA_M200_PAD 25
#define CREATIVEZV_PAD 26
#define PHILIPS_SA9200_PAD 27
-#define SANSA_C100_PAD 28
#define PHILIPS_HDD1630_PAD 29
#define MEIZU_M6SL_PAD 30
#define ONDAVX747_PAD 31
@@ -152,7 +144,6 @@
#define MA_PAD 56
#define SONY_NWZ_PAD 57
#define CREATIVE_ZEN_PAD 58
-#define SAMSUNG_YPZ5_PAD 59
#define IHIFI_PAD 60
#define SAMSUNG_YPR1_PAD 61
#define SAMSUNG_YH92X_PAD 62
@@ -167,6 +158,7 @@
#define FIIO_M3K_LINUX_PAD 71
#define EROSQ_PAD 72
#define FIIO_M3K_PAD 73
+#define SHANLING_Q1_PAD 74
/* CONFIG_REMOTE_KEYPAD */
#define H100_REMOTE 1
@@ -216,6 +208,8 @@
Usually application ports, and only
if the estimation is better that ours
(which it probably is) */
+#define CURRENT_MEASURE 8 /* Target can report battery charge and/or
+ * discharge current */
/* CONFIG_LCD */
#define LCD_SSD1815 1 /* as used by Sansa M200 and others */
#define LCD_S1D15E06 3 /* as used by iRiver H100 series */
@@ -228,21 +222,17 @@
#define LCD_IPODVIDEO 8 /* as used by iPod Video */
#define LCD_IPOD2BPP 9 /* as used by all fullsize greyscale iPods */
#define LCD_IPODMINI 10 /* as used by iPod Mini g1/g2 */
-#define LCD_IFP7XX 11 /* as used by iRiver iFP 7xx/8xx */
#define LCD_GIGABEAT 12
#define LCD_H10_20GB 13 /* as used by iriver H10 20Gb */
#define LCD_H10_5GB 14 /* as used by iriver H10 5Gb */
-#define LCD_TPJ1022 15 /* as used by Tatung Elio TPJ-1022 */
#define LCD_C200 17 /* as used by Sandisk Sansa c200 */
#define LCD_MROBE500 18 /* as used by Olympus M:Robe 500i */
#define LCD_MROBE100 19 /* as used by Olympus M:Robe 100 */
-#define LCD_LOGIKDAX 20 /* as used by Logik DAX - SSD1815 */
-#define LCD_IAUDIO67 21 /* as used by iAudio 6/7 - unknown */
#define LCD_CREATIVEZVM 22 /* as used by Creative Zen Vision:M */
#define LCD_TL0350A 23 /* as used by the iAudio M3 remote, treated as main LCD */
#define LCD_COWOND2 24 /* as used by Cowon D2 - LTV250QV, TCC7801 driver */
#define LCD_SA9200 25 /* as used by the Philips SA9200 */
-#define LCD_S6B33B2 26 /* as used by the Sansa c100 */
+#define LCD_S6B33B2 26 /* as used by the Samsumg YH820 */
#define LCD_HDD1630 27 /* as used by the Philips HDD1630 */
#define LCD_MEIZUM6 28 /* as used by the Meizu M6SP and M6SL (various models) */
#define LCD_ONDAVX747 29 /* as used by the Onda VX747 */
@@ -273,7 +263,6 @@
#define LCD_CREATIVEZENMOZAIC 56 /* as used by the Creative ZEN Mozaic (FGD0801) */
#define LCD_ILI9342C 57 /* another type of lcd used by HiFi E.T MA9/MA8 */
#define LCD_CREATIVEZENV 58 /* as used by the Creative Zen V (Plus) */
-#define LCD_SAMSUNGYPZ5 59 /* as used by Samsung YP-Z5 */
#define LCD_IHIFI 60 /* as used by IHIFI 760/960 */
#define LCD_CREATIVEZENXFISTYLE 61 /* as used by Creative Zen X-Fi Style */
#define LCD_SAMSUNGYPR1 62 /* as used by Samsung YP-R1 */
@@ -284,6 +273,8 @@
#define LCD_IHIFI770C 67 /* as used by IHIFI 770C */
#define LCD_IHIFI800 68 /* as used by IHIFI 800 */
#define LCD_FIIOM3K 69 /* as used by the FiiO M3K */
+#define LCD_SHANLING_Q1 70 /* as used by the Shanling Q1 */
+#define LCD_EROSQ 71 /* as used by the ErosQ (native) */
/* LCD_PIXELFORMAT */
#define HORIZONTAL_PACKING 1
@@ -314,7 +305,6 @@ Lyre prototype 1 */
#define I2C_S3C2440 7
#define I2C_PP5024 8 /* PP5024 style */
#define I2C_IMX31L 9
-#define I2C_TCC77X 10
#define I2C_TCC780X 11
#define I2C_DM320 12 /* DM320 style */
#define I2C_S5L8700 13
@@ -331,7 +321,6 @@ Lyre prototype 1 */
/* else HW controlled LED (iRiver H1x0) */
/* CONFIG_NAND */
-#define NAND_IFP7XX 1
#define NAND_TCC 2
#define NAND_SAMSUNG 3
#define NAND_CC 4 /* ChinaChip */
@@ -347,7 +336,6 @@ Lyre prototype 1 */
#define RTC_DS1339_DS3231 7 /* h1x0 RTC mod */
#define RTC_IMX31L 8
#define RTC_RX5X348AB 9
-#define RTC_TCC77X 10
#define RTC_TCC780X 11
#define RTC_MR100 12
#define RTC_MC13783 13 /* Freescale MC13783 PMIC */
@@ -361,6 +349,7 @@ Lyre prototype 1 */
#define RTC_STM41T62 21 /* ST M41T62 */
#define RTC_JZ4760 22 /* Ingenic Jz4760 */
#define RTC_X1000 23 /* Ingenic X1000 */
+#define RTC_CONNECT 24 /* Sansa Connect AVR */
/* USB On-the-go */
#define USBOTG_M66591 6591 /* M:Robe 500 */
@@ -391,6 +380,10 @@ Lyre prototype 1 */
#define IMX233_FREESCALE (1 << 0) /* Freescale I.MX233 nonstandard two-level MBR */
#define IMX233_CREATIVE (1 << 1) /* Creative MBLK windowing */
+/* CONFIG_BUFLIB_BACKEND */
+#define BUFLIB_BACKEND_MEMPOOL 0 /* Default memory pool backed buflib */
+#define BUFLIB_BACKEND_MALLOC 1 /* malloc() buflib (for debugging) */
+
/* now go and pick yours */
#if defined(IRIVER_H100)
#include "config/iriverh100.h"
@@ -420,8 +413,6 @@ Lyre prototype 1 */
#include "config/ipodnano2g.h"
#elif defined(IPOD_6G)
#include "config/ipod6g.h"
-#elif defined(IRIVER_IFP7XX)
-#include "config/iriverifp7xx.h"
#elif defined(GIGABEAT_F)
#include "config/gigabeatfx.h"
#elif defined(GIGABEAT_S)
@@ -438,18 +429,10 @@ Lyre prototype 1 */
#include "config/sansae200.h"
#elif defined(SANSA_C200)
#include "config/sansac200.h"
-#elif defined(SANSA_M200)
-#include "config/sansam200.h"
-#elif defined(TATUNG_TPJ1022)
-#include "config/tatungtpj1022.h"
#elif defined(MROBE_100)
#include "config/mrobe100.h"
#elif defined(MROBE_500)
#include "config/mrobe500.h"
-#elif defined(LOGIK_DAX)
-#include "config/logikdax.h"
-#elif defined(IAUDIO_7)
-#include "config/iaudio7.h"
#elif defined(COWON_D2)
#include "config/cowond2.h"
#elif defined(CREATIVE_ZVM)
@@ -468,8 +451,6 @@ Lyre prototype 1 */
#include "config/gogearhdd1630.h"
#elif defined(PHILIPS_HDD6330)
#include "config/gogearhdd6330.h"
-#elif defined(SANSA_C100)
-#include "config/sansac100.h"
#elif defined(MEIZU_M6SL)
#include "config/meizum6sl.h"
#elif defined(MEIZU_M6SP)
@@ -562,8 +543,6 @@ Lyre prototype 1 */
#include "config/sonynwze370.h"
#elif defined(SONY_NWZE360)
#include "config/sonynwze360.h"
-#elif defined(SAMSUNG_YPZ5)
-#include "config/samsungypz5.h"
#elif defined(IHIFI760)
#include "config/ihifi760.h"
#elif defined(IHIFI770)
@@ -614,10 +593,23 @@ Lyre prototype 1 */
#include "config/fiiom3k.h"
#elif defined(EROS_Q)
#include "config/aigoerosq.h"
+#elif defined(SHANLING_Q1)
+#include "config/shanlingq1.h"
+#elif defined(EROS_QN)
+#include "config/erosqnative.h"
#else
//#error "unknown hwardware platform!"
#endif
+#ifndef CONFIG_CPU
+#define CONFIG_CPU 0
+#endif
+
+// NOTE: should be placed before sim.h (where CONFIG_CPU is undefined)
+#if !(CONFIG_CPU >= PP5002 && CONFIG_CPU <= PP5022) && CODEC_SIZE >= 0x80000
+#define CODEC_AAC_SBR_DEC
+#endif
+
#ifdef __PCTOOL__
#undef CONFIG_CPU
#define CONFIG_CPU 0
@@ -627,6 +619,10 @@ Lyre prototype 1 */
#undef CONFIG_STORAGE
#endif
+#ifndef CONFIG_BUFLIB_BACKEND
+# define CONFIG_BUFLIB_BACKEND BUFLIB_BACKEND_MEMPOOL
+#endif
+
#ifdef APPLICATION
#define CONFIG_CPU 0
#endif
@@ -634,6 +630,9 @@ Lyre prototype 1 */
/* keep this include after the target configs */
#ifdef SIMULATOR
#include "config/sim.h"
+#ifndef HAVE_POWEROFF_WHILE_CHARGING
+ #define HAVE_POWEROFF_WHILE_CHARGING
+#endif
#endif
#ifndef CONFIG_PLATFORM
@@ -666,11 +665,6 @@ Lyre prototype 1 */
#define CPU_S5L870X
#endif
-/* define for all cpus from TCC77X family */
-#if (CONFIG_CPU == TCC771L) || (CONFIG_CPU == TCC773L) || (CONFIG_CPU == TCC770)
-#define CPU_TCC77X
-#endif
-
/* define for all cpus from TCC780 family */
#if (CONFIG_CPU == TCC7801)
#define CPU_TCC780X
@@ -737,15 +731,24 @@ Lyre prototype 1 */
#define BATTERY_CAPACITY_DEFAULT 0
#endif
+#ifndef BATTERY_CAPACITY_MIN
+#define BATTERY_CAPACITY_MIN BATTERY_CAPACITY_DEFAULT
+#endif
+
+#ifndef BATTERY_CAPACITY_MAX
+#define BATTERY_CAPACITY_MAX BATTERY_CAPACITY_DEFAULT
+#endif
+
#ifndef BATTERY_CAPACITY_INC
#define BATTERY_CAPACITY_INC 0
#endif
#ifdef HAVE_RDS_CAP
/* combinable bitflags */
-#define RDS_CFG_ISR 0x1 /* uses ISR to process packets */
+/* 0x01 can be reused, was RDS_CFG_ISR */
#define RDS_CFG_PROCESS 0x2 /* uses raw packet processing */
#define RDS_CFG_PUSH 0x4 /* pushes processed information */
+#define RDS_CFG_POLL 0x8 /* tuner driver provides a polling function */
#ifndef CONFIG_RDS
#define CONFIG_RDS RDS_CFG_PROCESS /* thread processing+raw processing */
#endif /* CONFIG_RDS */
@@ -786,6 +789,11 @@ Lyre prototype 1 */
#define LCD_SPLIT_LINES 0
#endif
+/* Most displays have a horizontal stride */
+#ifndef LCD_STRIDEFORMAT
+# define LCD_STRIDEFORMAT HORIZONTAL_STRIDE
+#endif
+
/* Simulator LCD dimensions. Set to standard dimensions if undefined */
#ifndef SIM_LCD_WIDTH
#define SIM_LCD_WIDTH LCD_WIDTH
@@ -883,6 +891,12 @@ Lyre prototype 1 */
#define HAVE_MULTIBOOT
#endif
+/* The lowest numbered volume to read a multiboot redirect from; default is to
+ * allow any volume but some targets may wish to exclude the internal drive. */
+#if defined(HAVE_MULTIBOOT) && !defined(MULTIBOOT_MIN_VOLUME)
+# define MULTIBOOT_MIN_VOLUME 0
+#endif
+
#ifndef NUM_DRIVES
#define NUM_DRIVES 1
#endif
@@ -930,6 +944,13 @@ Lyre prototype 1 */
#define INCLUDE_TIMEOUT_API
#define USB_DRIVER_CLOSE
#endif
+#if defined(HAVE_USBSTACK) && CONFIG_USBOTG == USBOTG_TNETV105
+#define INCLUDE_TIMEOUT_API
+#define USB_DRIVER_CLOSE
+#endif
+#if CONFIG_CPU == X1000
+#define USB_DRIVER_CLOSE
+#endif
#endif
#else /* !BOOTLOADER */
@@ -966,6 +987,9 @@ Lyre prototype 1 */
#define USB_DETECT_BY_REQUEST
#elif CONFIG_USBOTG == USBOTG_RK27XX
#define USB_DETECT_BY_REQUEST
+#elif CONFIG_USBOTG == USBOTG_TNETV105
+#define USB_STATUS_BY_EVENT
+#define USB_DETECT_BY_REQUEST
#endif /* CONFIG_USB == */
#endif /* HAVE_USBSTACK */
@@ -976,8 +1000,7 @@ Lyre prototype 1 */
|| defined(CPU_S5L870X) || (CONFIG_CPU == S3C2440) \
|| defined(APPLICATION) || (CONFIG_CPU == PP5002) \
|| (CONFIG_CPU == RK27XX) || (CONFIG_CPU == IMX233) || \
- (defined(HAVE_LCD_COLOR) && \
- (!defined(LCD_STRIDEFORMAT) || (LCD_STRIDEFORMAT != VERTICAL_STRIDE)))
+ (defined(HAVE_LCD_COLOR) && (LCD_STRIDEFORMAT == HORIZONTAL_STRIDE))
#define HAVE_SEMAPHORE_OBJECTS
#endif
@@ -991,14 +1014,25 @@ Lyre prototype 1 */
#define ROCKBOX_STRICT_ALIGN 1
#endif
+/*
+ * These macros are for switching on unified syntax in inline assembly.
+ * Older versions of GCC emit assembly in divided syntax with no option
+ * to enable unified syntax.
+ *
+ * FIXME: This needs to be looked at after the toolchain bump
+ */
+#define BEGIN_ARM_ASM_SYNTAX_UNIFIED ".syntax unified\n"
+#define END_ARM_ASM_SYNTAX_UNIFIED ".syntax divided\n"
+
#if defined(CPU_ARM) && defined(__ASSEMBLER__)
+.syntax unified
/* ARMv4T doesn't switch the T bit when popping pc directly, we must use BX */
.macro ldmpc cond="", order="ia", regs
#if ARM_ARCH == 4 && defined(USE_THUMB)
- ldm\cond\order sp!, { \regs, lr }
+ ldm\order\cond sp!, { \regs, lr }
bx\cond lr
#else
- ldm\cond\order sp!, { \regs, pc }
+ ldm\order\cond sp!, { \regs, pc }
#endif
.endm
.macro ldrpc cond=""
@@ -1064,7 +1098,7 @@ Lyre prototype 1 */
#if (defined(CPU_PP) || (CONFIG_CPU == AS3525) || (CONFIG_CPU == AS3525v2) || \
(CONFIG_CPU == IMX31L) || (CONFIG_CPU == IMX233) || \
- (CONFIG_CPU == RK27XX) || defined(CPU_COLDFIRE)) \
+ (CONFIG_CPU == RK27XX) || defined(CPU_MIPS) || defined(CPU_COLDFIRE)) \
&& (CONFIG_PLATFORM & PLATFORM_NATIVE) && !defined(BOOTLOADER)
/* Functions that have INIT_ATTR attached are NOT guaranteed to survive after
* root_menu() has been called. Their code may be overwritten by other data or
@@ -1076,8 +1110,16 @@ Lyre prototype 1 */
* only while main() runs), otherwise things may go wild,
* from crashes to freezes to exploding daps.
*/
-#define INIT_ATTR __attribute__ ((section(".init")))
-#define INITDATA_ATTR __attribute__ ((section(".initdata")))
+
+
+#if defined(__APPLE__) && defined(__MACH__)
+ #define INIT_ATTR __attribute__((section ("__INIT,.init")))
+ #define INITDATA_ATTR __attribute__((section ("__INITDATA,.initdata")))
+#else
+ #define INIT_ATTR __attribute__ ((section(".init")))
+ #define INITDATA_ATTR __attribute__ ((section(".initdata")))
+#endif
+
#define HAVE_INIT_ATTR
#else
#define INIT_ATTR
@@ -1179,6 +1221,11 @@ Lyre prototype 1 */
#define INCLUDE_TIMEOUT_API
#endif /* HAVE_USB_CHARGING_ENABLE && HAVE_USBSTACK */
+#if defined(USB_STATUS_BY_EVENT) && defined(HAVE_USBSTACK)
+/* Status by event requires timeout for debouncing */
+# define INCLUDE_TIMEOUT_API
+#endif
+
#ifndef SIMULATOR
#if defined(HAVE_USBSTACK) || (CONFIG_STORAGE & STORAGE_NAND) || (CONFIG_STORAGE & STORAGE_RAMDISK)
#define STORAGE_GET_INFO
@@ -1193,19 +1240,26 @@ Lyre prototype 1 */
/* Define the implemented USB transport classes */
#if CONFIG_USBOTG == USBOTG_ISP1583
#define USB_HAS_BULK
-#elif (CONFIG_USBOTG == USBOTG_ARC) || \
+#define USB_LEGACY_CONTROL_API
+#elif (CONFIG_USBOTG == USBOTG_DESIGNWARE)
+#define USB_HAS_BULK
+#define USB_HAS_INTERRUPT
+#elif (CONFIG_USBOTG == USBOTG_ARC) || \
(CONFIG_USBOTG == USBOTG_JZ4740) || \
(CONFIG_USBOTG == USBOTG_JZ4760) || \
(CONFIG_USBOTG == USBOTG_M66591) || \
- (CONFIG_USBOTG == USBOTG_DESIGNWARE) || \
(CONFIG_USBOTG == USBOTG_AS3525) || \
- (CONFIG_USBOTG == USBOTG_RK27XX)
+ (CONFIG_USBOTG == USBOTG_RK27XX) || \
+ (CONFIG_USBOTG == USBOTG_TNETV105)
#define USB_HAS_BULK
#define USB_HAS_INTERRUPT
-#elif defined(CPU_TCC780X) || defined(CPU_TCC77X)
+#define USB_LEGACY_CONTROL_API
+#elif defined(CPU_TCC780X)
#define USB_HAS_BULK
+#define USB_LEGACY_CONTROL_API
#elif CONFIG_USBOTG == USBOTG_S3C6400X
#define USB_HAS_BULK
+#define USB_LEGACY_CONTROL_API
//#define USB_HAS_INTERRUPT -- seems to be broken
#endif /* CONFIG_USBOTG */
@@ -1219,7 +1273,7 @@ Lyre prototype 1 */
/* enable usb storage for targets that do bootloader usb */
#if defined(HAVE_BOOTLOADER_USB_MODE) || \
- defined(CREATIVE_ZVx) || defined(CPU_TCC77X) || defined(CPU_TCC780X) || \
+ defined(CREATIVE_ZVx) || defined(CPU_TCC780X) || \
CONFIG_USBOTG == USBOTG_JZ4740 || CONFIG_USBOTG == USBOTG_AS3525 || \
CONFIG_USBOTG == USBOTG_S3C6400X || CONFIG_USBOTG == USBOTG_DESIGNWARE || \
CONFIG_USBOTG == USBOTG_JZ4760
@@ -1281,6 +1335,14 @@ Lyre prototype 1 */
#endif /* SIMULATOR */
#endif /* default SDL SW volume conditions */
+#if !defined(BOOTLOADER) || defined(HAVE_BOOTLOADER_SCREENDUMP)
+# define HAVE_SCREENDUMP
+#endif
+
+#if !defined(BOOTLOADER) && MEMORYSIZE > 2
+# define HAVE_PERCEPTUAL_VOLUME
+#endif
+
/* null audiohw setting macro for when codec header is included for reasons
other than audio support */
#define AUDIOHW_SETTING(name, us, nd, st, minv, maxv, defv, expr...)
diff --git a/firmware/export/config/aigoerosq.h b/firmware/export/config/aigoerosq.h
index 2d0f7a0cdf..bfaa323968 100644
--- a/firmware/export/config/aigoerosq.h
+++ b/firmware/export/config/aigoerosq.h
@@ -15,6 +15,7 @@
/* define this if you have access to the quickscreen */
#define HAVE_QUICKSCREEN
+#define HAVE_HOTKEY
#define HAVE_HEADPHONE_DETECTION
#define HAVE_LINEOUT_DETECTION
diff --git a/firmware/export/config/android.h b/firmware/export/config/android.h
index 7d3355ef9d..ac0e2141a1 100644
--- a/firmware/export/config/android.h
+++ b/firmware/export/config/android.h
@@ -51,9 +51,6 @@
#define HAVE_TOUCHSCREEN
#define HAVE_BUTTON_DATA
-/* define this if you have RTC RAM available for settings */
-//#define HAVE_RTC_RAM
-
/* define this if you have a real-time clock */
#define CONFIG_RTC APPLICATION
diff --git a/firmware/export/config/cowond2.h b/firmware/export/config/cowond2.h
index 81a2a85018..51b5495680 100644
--- a/firmware/export/config/cowond2.h
+++ b/firmware/export/config/cowond2.h
@@ -90,9 +90,6 @@
/* The D2 has either a PCF50606 or PCF50635, RTC_D2 handles both */
#define CONFIG_RTC RTC_D2
-/* define this if you have RTC RAM available for settings */
-//#define HAVE_RTC_RAM
-
/* Define this if you have a software controlled poweroff */
#define HAVE_SW_POWEROFF
diff --git a/firmware/export/config/erosqnative.h b/firmware/export/config/erosqnative.h
new file mode 100644
index 0000000000..4f87282f1f
--- /dev/null
+++ b/firmware/export/config/erosqnative.h
@@ -0,0 +1,146 @@
+/*
+ * This config file is for the AIGO EROS Q / EROS K (and its clones)
+ */
+
+/* For Rolo and boot loader */
+#define MODEL_NAME "AIGO Eros Q Native"
+#define MODEL_NUMBER 116
+#define BOOTFILE_EXT "erosq"
+#define BOOTFILE "rockbox." BOOTFILE_EXT
+#define BOOTDIR "/.rockbox"
+#define FIRMWARE_OFFSET_FILE_CRC 0
+#define FIRMWARE_OFFSET_FILE_DATA 8
+
+/* CPU defines */
+#define CONFIG_CPU X1000
+#define X1000_EXCLK_FREQ 24000000
+#define CPU_FREQ 1008000000
+#define HAVE_FPU
+
+#ifndef SIMULATOR
+#define TIMER_FREQ X1000_EXCLK_FREQ
+#endif
+
+/* kernel defines */
+#define INCLUDE_TIMEOUT_API
+#define HAVE_SEMAPHORE_OBJECTS
+
+/* drivers */
+#define HAVE_I2C_ASYNC
+
+/* Buffers for plugsins and codecs */
+#define PLUGIN_BUFFER_SIZE 0x200000 /* 2 MiB */
+#define CODEC_SIZE 0x100000 /* 1 MiB */
+
+/* LCD defines */
+#define CONFIG_LCD LCD_EROSQ
+#define LCD_WIDTH 320
+#define LCD_HEIGHT 240
+#define LCD_DEPTH 16 /* Future Improvement: 18 or 24 bpp if display supports it */
+#define LCD_PIXELFORMAT RGB565
+/* sqrt(240^2 + 320^2) / 2.0 = 200 */
+#define LCD_DPI 200
+#define HAVE_LCD_COLOR
+#define HAVE_LCD_BITMAP
+#define HAVE_LCD_ENABLE
+#define HAVE_LCD_SHUTDOWN
+#define LCD_X1000_FASTSLEEP
+//#define LCD_X1000_DMA_WAITFORFRAME
+
+#define HAVE_BACKLIGHT
+#define HAVE_BACKLIGHT_BRIGHTNESS
+#define MIN_BRIGHTNESS_SETTING 1
+#define MAX_BRIGHTNESS_SETTING 255
+#define BRIGHTNESS_STEP 5
+#define DEFAULT_BRIGHTNESS_SETTING 70
+#define CONFIG_BACKLIGHT_FADING BACKLIGHT_FADING_SW_SETTING
+
+/* RTC settings */
+#define CONFIG_RTC RTC_X1000
+
+/* Codec / audio hardware defines */
+#define HW_SAMPR_CAPS SAMPR_CAP_ALL_192
+#define HAVE_EROS_QN_CODEC
+#define HAVE_SW_TONE_CONTROLS
+#define HAVE_SW_VOLUME_CONTROL
+#define AUDIOHW_CAPS (FILTER_ROLL_OFF_CAP)
+#define AUDIOHW_HAVE_SHORT_ROLL_OFF
+
+/* use high-bitdepth volume scaling */
+#define PCM_NATIVE_BITDEPTH 24
+
+/* Button defines */
+#define CONFIG_KEYPAD EROSQ_PAD
+#define HAVE_SCROLLWHEEL
+#define HAVE_HEADPHONE_DETECTION
+#define HAVE_LINEOUT_DETECTION
+
+/* Storage defines */
+#define CONFIG_STORAGE STORAGE_SD
+#define HAVE_HOTSWAP
+#define HAVE_HOTSWAP_STORAGE_AS_MAIN
+#define HAVE_MULTIDRIVE
+#define HAVE_MULTIVOLUME
+#define NUM_DRIVES 1
+#define STORAGE_WANTS_ALIGN
+#define STORAGE_NEEDS_BOUNCE_BUFFER
+
+/* Power management */
+#define CONFIG_BATTERY_MEASURE (VOLTAGE_MEASURE/*|CURRENT_MEASURE*/)
+#define CONFIG_CHARGING CHARGING_MONITOR
+#define HAVE_SW_POWEROFF
+
+#ifndef SIMULATOR
+#define HAVE_AXP_PMU 192
+#define HAVE_POWEROFF_WHILE_CHARGING
+#endif
+
+/* Battery */
+#define BATTERY_TYPES_COUNT 1
+#define BATTERY_CAPACITY_DEFAULT 1300 /* default battery capacity */
+#define BATTERY_CAPACITY_MIN 1300 /* min. capacity selectable */
+#define BATTERY_CAPACITY_MAX 1300 /* max. capacity selectable */
+#define BATTERY_CAPACITY_INC 0 /* capacity increment */
+
+#define CURRENT_NORMAL 100 // 1.7mA * 60s
+#define CURRENT_BACKLIGHT 180
+#define CURRENT_MAX_CHG 500 // bursts higher if needed
+
+/* Multiboot */
+#define HAVE_BOOTDATA
+#define BOOT_REDIR "rockbox_main.aigo_erosqn"
+
+/* USB support */
+#ifndef SIMULATOR
+#define CONFIG_USBOTG USBOTG_DESIGNWARE
+#define USB_DW_TURNAROUND 5
+#define HAVE_USBSTACK
+#define USB_VENDOR_ID 0xc502
+#define USB_PRODUCT_ID 0x0023
+#define USB_DEVBSS_ATTR __attribute__((aligned(32)))
+#define HAVE_USB_POWER
+#define HAVE_USB_CHARGING_ENABLE
+#define HAVE_USB_CHARGING_IN_THREAD
+#define TARGET_USB_CHARGING_DEFAULT USB_CHARGING_FORCE
+#define HAVE_BOOTLOADER_USB_MODE
+/* This appears to improve transfer performance (the default is 64 KiB).
+ * Going any higher doesn't help but we're still slower than the OF. */
+#define USB_READ_BUFFER_SIZE (128 * 1024)
+#define USB_WRITE_BUFFER_SIZE (128 * 1024)
+#endif
+
+#ifdef BOOTLOADER
+/* Ignore on any key can cause surprising USB issues in the bootloader */
+# define USBPOWER_BTN_IGNORE (~(BUTTON_PREV|BUTTON_NEXT))
+#endif
+
+/* Rockbox capabilities */
+#define HAVE_VOLUME_IN_LIST
+#define HAVE_FAT16SUPPORT
+#define HAVE_ALBUMART
+#define HAVE_BMP_SCALING
+#define HAVE_JPEG
+#define HAVE_TAGCACHE
+#define HAVE_QUICKSCREEN
+#define HAVE_HOTKEY
+#define HAVE_BOOTLOADER_SCREENDUMP
diff --git a/firmware/export/config/fiiom3k.h b/firmware/export/config/fiiom3k.h
index 99285728a2..86a9b05402 100644
--- a/firmware/export/config/fiiom3k.h
+++ b/firmware/export/config/fiiom3k.h
@@ -10,25 +10,21 @@
/* CPU defines */
#define CONFIG_CPU X1000
#define X1000_EXCLK_FREQ 24000000
+#define CPU_FREQ 1008000000
#ifndef SIMULATOR
#define TIMER_FREQ X1000_EXCLK_FREQ
#endif
-#define CPU_FREQ 1008000000
-#define CPUFREQ_MAX CPU_FREQ
-/* TODO: figure out if this does in fact affect power consumption. */
-#define CPUFREQ_DEFAULT (CPUFREQ_MAX/4)
-#define CPUFREQ_NORMAL (CPUFREQ_MAX/4)
-#define HAVE_ADJUSTABLE_CPU_FREQ
-#define HAVE_GUI_BOOST
-
/* Kernel defines */
#define INCLUDE_TIMEOUT_API
#define HAVE_SEMAPHORE_OBJECTS
/* Drivers */
#define HAVE_I2C_ASYNC
+#define HAVE_FT6x06
+#define FT6x06_SWAP_AXES
+#define FT6x06_NUM_POINTS 1
/* Buffer for plugins and codecs. */
#define PLUGIN_BUFFER_SIZE 0x200000 /* 2 MiB */
@@ -59,11 +55,16 @@
/* Codec / audio hardware defines */
#define HW_SAMPR_CAPS SAMPR_CAP_ALL_192
+#define REC_SAMPR_CAPS (SAMPR_CAP_ALL_96 & ~SAMPR_CAP_64)
+#define INPUT_SRC_CAPS SRC_CAP_MIC
+#define AUDIOHW_CAPS (FILTER_ROLL_OFF_CAP|POWER_MODE_CAP|MIC_GAIN_CAP)
+#define HAVE_RECORDING
+#define HAVE_RECORDING_WITHOUT_MONITORING
#define HAVE_AK4376
+#define HAVE_X1000_ICODEC_REC
#define HAVE_SW_TONE_CONTROLS
#define HAVE_SW_VOLUME_CONTROL
-
-/* TODO: Need to implement recording */
+#define DEFAULT_REC_MIC_GAIN 12
/* Button defines */
#define CONFIG_KEYPAD FIIO_M3K_PAD
@@ -83,6 +84,7 @@
#define HAVE_HOTSWAP
#define HAVE_HOTSWAP_STORAGE_AS_MAIN
#define HAVE_MULTIDRIVE
+#define HAVE_MULTIVOLUME
#define NUM_DRIVES 1
#define STORAGE_WANTS_ALIGN
#define STORAGE_NEEDS_BOUNCE_BUFFER
@@ -92,12 +94,12 @@
/* TODO: implement HAVE_RTC_ALARM */
/* Power management */
-#define CONFIG_BATTERY_MEASURE VOLTAGE_MEASURE
+#define CONFIG_BATTERY_MEASURE (VOLTAGE_MEASURE|CURRENT_MEASURE)
#define CONFIG_CHARGING CHARGING_MONITOR
#define HAVE_SW_POWEROFF
#ifndef SIMULATOR
-#define HAVE_AXP173
+#define HAVE_AXP_PMU 192
#define HAVE_POWEROFF_WHILE_CHARGING
#endif
@@ -108,9 +110,32 @@
#define BATTERY_CAPACITY_INC 0
#define BATTERY_TYPES_COUNT 1
-/* USB is still TODO. */
+/* Multiboot */
+#define HAVE_BOOTDATA
+#define BOOT_REDIR "rockbox_main.fiio_m3k"
+
+/* USB support */
#ifndef SIMULATOR
-#define USB_NONE
+#define CONFIG_USBOTG USBOTG_DESIGNWARE
+#define USB_DW_TURNAROUND 5
+#define HAVE_USBSTACK
+#define USB_VENDOR_ID 0x2972
+#define USB_PRODUCT_ID 0x0003
+#define USB_DEVBSS_ATTR __attribute__((aligned(32)))
+#define HAVE_USB_POWER
+#define HAVE_USB_CHARGING_ENABLE
+#define HAVE_USB_CHARGING_IN_THREAD
+#define TARGET_USB_CHARGING_DEFAULT USB_CHARGING_FORCE
+#define HAVE_BOOTLOADER_USB_MODE
+/* This appears to improve transfer performance (the default is 64 KiB).
+ * Going any higher doesn't help but we're still slower than the OF. */
+#define USB_READ_BUFFER_SIZE (128 * 1024)
+#define USB_WRITE_BUFFER_SIZE (128 * 1024)
+#endif
+
+#ifdef BOOTLOADER
+/* Ignore on any key can cause surprising USB issues in the bootloader */
+# define USBPOWER_BTN_IGNORE (~(BUTTON_VOL_UP|BUTTON_VOL_DOWN))
#endif
/* Rockbox capabilities */
@@ -123,3 +148,4 @@
#define HAVE_QUICKSCREEN
#define HAVE_HOTKEY
#define AB_REPEAT_ENABLE
+#define HAVE_BOOTLOADER_SCREENDUMP
diff --git a/firmware/export/config/fiiom3klinux.h b/firmware/export/config/fiiom3klinux.h
index fea464c2de..22b996f6bf 100644
--- a/firmware/export/config/fiiom3klinux.h
+++ b/firmware/export/config/fiiom3klinux.h
@@ -69,7 +69,7 @@
#define CONFIG_RTC APPLICATION
/* The number of bytes reserved for loadable codecs */
-#define CODEC_SIZE 0x80000
+#define CODEC_SIZE 0x100000
/* The number of bytes reserved for loadable plugins */
#define PLUGIN_BUFFER_SIZE 0x100000
diff --git a/firmware/export/config/gigabeats.h b/firmware/export/config/gigabeats.h
index 8d82b44065..edf76d0e98 100644
--- a/firmware/export/config/gigabeats.h
+++ b/firmware/export/config/gigabeats.h
@@ -121,7 +121,6 @@
/* Define this if you have a SI4700 fm radio tuner */
#define CONFIG_TUNER SI4700
#define HAVE_RDS_CAP
-#define CONFIG_RDS (RDS_CFG_ISR | RDS_CFG_PROCESS)
/* define this if you can flip your LCD */
#define HAVE_LCD_FLIP
@@ -164,7 +163,7 @@
#define GPIO_EVENT_MASK (USE_GPIO1_EVENTS)
/* Define this if target has an additional number of threads specific to it */
-#define TARGET_EXTRA_THREADS 1
+#define TARGET_EXTRA_THREADS 2 /* one is for RDS */
/* Type of mobile power - check this out */
#define BATTERY_CAPACITY_DEFAULT 700 /* default battery capacity */
diff --git a/firmware/export/config/hibylinux.h b/firmware/export/config/hibylinux.h
index f1e52917ba..ad168c9c01 100644
--- a/firmware/export/config/hibylinux.h
+++ b/firmware/export/config/hibylinux.h
@@ -34,7 +34,7 @@
#define HAVE_TAGCACHE
/* The number of bytes reserved for loadable codecs */
-#define CODEC_SIZE 0x80000
+#define CODEC_SIZE 0x100000
/* The number of bytes reserved for loadable plugins */
#define PLUGIN_BUFFER_SIZE 0x100000
diff --git a/firmware/export/config/iaudio7.h b/firmware/export/config/iaudio7.h
deleted file mode 100644
index 698ff13724..0000000000
--- a/firmware/export/config/iaudio7.h
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * This config file is for the Iaudio7 series
- */
-
-/* For Rolo and boot loader */
-#define MODEL_NUMBER 32
-#define MODEL_NAME "Cowon iAudio7"
-
-/* define this if you have recording possibility */
-#define HAVE_RECORDING
-
-/* Define bitmask of input sources - recordable bitmask can be defined
- explicitly if different */
-#define INPUT_SRC_CAPS (SRC_CAP_MIC | SRC_CAP_LINEIN | SRC_CAP_FMRADIO)
-
-/* FM Tuner */
-#define CONFIG_TUNER LV24020LP
-#define HAVE_TUNER_PWR_CTRL
-
-/* Define this for FM radio input available */
-#define HAVE_FMRADIO_IN
-
-/* define hardware samples rate caps mask */
-#define HW_SAMPR_CAPS (/*SAMPR_CAP_88 | */SAMPR_CAP_44/* | SAMPR_CAP_22 | SAMPR_CAP_11*/)
-
-/* define the bitmask of recording sample rates */
-#define REC_SAMPR_CAPS (SAMPR_CAP_44/* | SAMPR_CAP_22 | SAMPR_CAP_11*/)
-
-
-
-
-/* define this if you have a colour LCD */
-#define HAVE_LCD_COLOR
-
-/* define this if you can flip your LCD */
-//#define HAVE_LCD_FLIP
-
-/* define this if you can invert the colours on your LCD */
-//#define HAVE_LCD_INVERT
-
-/* define this if you want album art for this target */
-#define HAVE_ALBUMART
-
-/* define this to enable bitmap scaling */
-#define HAVE_BMP_SCALING
-
-/* define this to enable JPEG decoding */
-#define HAVE_JPEG
-
-/* define this if you have access to the quickscreen */
-#define HAVE_QUICKSCREEN
-
-/* define this if you have LCD enable function */
-#define HAVE_LCD_ENABLE
-
-/* define this if you would like tagcache to build on this target */
-#define HAVE_TAGCACHE
-
-#define HAVE_FAT16SUPPORT
-
-#if 0 && !defined(SIMULATOR) /* Enable for USB driver test */
-#define HAVE_USBSTACK
-#define USB_VENDOR_ID 0x0e21
-#define USB_PRODUCT_ID 0x0750
-#endif
-
-/* define this if you have a flash memory storage */
-#define HAVE_FLASH_STORAGE
-
-#define CONFIG_STORAGE STORAGE_NAND
-
-#define CONFIG_NAND NAND_TCC
-
-/* LCD dimensions */
-#define LCD_WIDTH 160
-#define LCD_HEIGHT 128
-/* sqrt(160^2 + 128^2) / 1.3 = 157.6 */
-#define LCD_DPI 158
-/* 16bits for now... */
-#define LCD_DEPTH 16 /* 262144 colours */
-#define LCD_PIXELFORMAT RGB565 /*rgb565*/
-
-/*#define LCD_PIXELFORMAT VERTICAL_PACKING*/
-
-/* define this to indicate your device's keypad */
-#define CONFIG_KEYPAD IAUDIO67_PAD
-
-/* #define HAVE_BUTTON_DATA */
-
-/* define this if you have a real-time clock */
-#define CONFIG_RTC RTC_PCF50606
-
-/* define this if you have RTC RAM available for settings */
-//#define HAVE_RTC_RAM
-
-/* Define this if you have a software controlled poweroff */
-#define HAVE_SW_POWEROFF
-
-/* Reduce Tremor's ICODE usage */
-#define ICODE_ATTR_TREMOR_NOT_MDCT
-
-/* The number of bytes reserved for loadable codecs */
-#define CODEC_SIZE 0x100000
-
-/* The number of bytes reserved for loadable plugins */
-#define PLUGIN_BUFFER_SIZE 0x80000
-
-#define AB_REPEAT_ENABLE
-
-
-
-
-/* The iaudio7 uses built-in WM8731 codec */
-#define HAVE_WM8731
-/* Codec is slave on serial bus */
-#define CODEC_SLAVE
-
-/* WM8731 has no tone controls, so we use the software ones */
-#define HAVE_SW_TONE_CONTROLS
-
-/* Define this for LCD backlight available */
-#define HAVE_BACKLIGHT
-
-#define CONFIG_I2C I2C_TCC77X
-
-#define BATTERY_CAPACITY_DEFAULT 540 /* default battery capacity */
-#define BATTERY_CAPACITY_MIN 540 /* min. capacity selectable */
-#define BATTERY_CAPACITY_MAX 540 /* max. capacity selectable */
-#define BATTERY_CAPACITY_INC 50 /* capacity increment */
-#define BATTERY_TYPES_COUNT 1 /* only one type */
-
-#define CONFIG_BATTERY_MEASURE VOLTAGE_MEASURE
-
-#define CONFIG_CHARGING CHARGING_SIMPLE
-
-/* Define this if you have a TCC770 */
-#define CONFIG_CPU TCC770
-
-/* Define this if you have ATA power-off control */
-#define HAVE_ATA_POWER_OFF
-
-/* Define this to the CPU frequency */
-#define CPU_FREQ 120000000
-
-/* Offset ( in the firmware file's header ) to the file CRC */
-#define FIRMWARE_OFFSET_FILE_CRC 0
-
-/* Offset ( in the firmware file's header ) to the real data */
-#define FIRMWARE_OFFSET_FILE_DATA 8
-
-#define CONFIG_LCD LCD_IAUDIO67
-
-#define BOOTFILE_EXT "iaudio"
-#define BOOTFILE "rockbox." BOOTFILE_EXT
-#define BOOTDIR "/.rockbox"
-
-#ifdef BOOTLOADER
-#define TCCBOOT
-#endif
-
-#define IRAM_LCDFRAMEBUFFER IBSS_ATTR /* put the lcd frame buffer in IRAM */
-
-/* Define this if a programmable hotkey is mapped */
-//#define HAVE_HOTKEY
diff --git a/firmware/export/config/iaudiox5.h b/firmware/export/config/iaudiox5.h
index 0164cd20fa..cae83dd952 100644
--- a/firmware/export/config/iaudiox5.h
+++ b/firmware/export/config/iaudiox5.h
@@ -151,6 +151,7 @@
#define CURRENT_NORMAL 65 /*2250mah/35h = 65 ma*/
#define CURRENT_BACKLIGHT 25
#define CURRENT_REMOTE 8 /* additional current when remote connected */
+#define CURRENT_RECORD 2 /* FIXME: placeholder value */
/* Define this if your LCD can set contrast */
#define HAVE_LCD_CONTRAST
diff --git a/firmware/export/config/ibassodx50.h b/firmware/export/config/ibassodx50.h
index bb4b2809e5..2161d9f3e3 100644
--- a/firmware/export/config/ibassodx50.h
+++ b/firmware/export/config/ibassodx50.h
@@ -97,11 +97,9 @@
/* Which backlight fading type? */
#define CONFIG_BACKLIGHT_FADING BACKLIGHT_FADING_SW_SETTING
-
-
#define HAVE_SW_TONE_CONTROLS
#define HAVE_SW_VOLUME_CONTROL
-#define HW_SAMPR_CAPS SAMPR_CAP_ALL_96
+#define HW_SAMPR_CAPS SAMPR_CAP_ALL_192
//#define HAVE_MULTIMEDIA_KEYS
#define CONFIG_KEYPAD DX50_PAD
@@ -111,7 +109,7 @@
#define BATTERY_CAPACITY_DEFAULT 2100 /* default battery capacity */
#define BATTERY_CAPACITY_MIN 1700 /* min. capacity selectable */
-#define BATTERY_CAPACITY_MAX 3200 /* max. capacity selectable */
+#define BATTERY_CAPACITY_MAX 7300 /* max. capacity selectable */
#define BATTERY_CAPACITY_INC 50 /* capacity increment */
#define BATTERY_TYPES_COUNT 1 /* only one type */
diff --git a/firmware/export/config/ibassodx90.h b/firmware/export/config/ibassodx90.h
index cd7ddf78cd..570a8d2a35 100644
--- a/firmware/export/config/ibassodx90.h
+++ b/firmware/export/config/ibassodx90.h
@@ -96,11 +96,9 @@
/* Which backlight fading type? */
#define CONFIG_BACKLIGHT_FADING BACKLIGHT_FADING_SW_SETTING
-
-
#define HAVE_SW_TONE_CONTROLS
#define HAVE_SW_VOLUME_CONTROL
-#define HW_SAMPR_CAPS SAMPR_CAP_ALL_96
+#define HW_SAMPR_CAPS SAMPR_CAP_ALL_192
//#define HAVE_MULTIMEDIA_KEYS
#define CONFIG_KEYPAD DX50_PAD
@@ -110,7 +108,7 @@
#define BATTERY_CAPACITY_DEFAULT 2100 /* default battery capacity */
#define BATTERY_CAPACITY_MIN 1700 /* min. capacity selectable */
-#define BATTERY_CAPACITY_MAX 3200 /* max. capacity selectable */
+#define BATTERY_CAPACITY_MAX 7300 /* max. capacity selectable */
#define BATTERY_CAPACITY_INC 50 /* capacity increment */
#define BATTERY_TYPES_COUNT 1 /* only one type */
diff --git a/firmware/export/config/ipod6g.h b/firmware/export/config/ipod6g.h
index fdf7e8e516..b03b1131bd 100644
--- a/firmware/export/config/ipod6g.h
+++ b/firmware/export/config/ipod6g.h
@@ -133,11 +133,9 @@
/* Define this if you have a software controlled poweroff */
#define HAVE_SW_POWEROFF
-/* The number of bytes reserved for loadable codecs */
-#define CODEC_SIZE 0x100000
-
-/* The number of bytes reserved for loadable plugins */
-#define PLUGIN_BUFFER_SIZE 0x80000
+/* Buffer for plugins and codecs. */
+#define PLUGIN_BUFFER_SIZE 0x200000 /* 2 MiB */
+#define CODEC_SIZE 0x100000 /* 1 MiB */
/* 6g has a standard battery of 550mAh, except for the thick 6g (2007 160gb)
* which has a standard battery of 850mAh.
@@ -158,6 +156,7 @@
/* define current usage levels */
#define CURRENT_NORMAL 18 /* playback @48MHz clock, backlight off */
#define CURRENT_BACKLIGHT 23 /* maximum brightness */
+#define CURRENT_RECORD 2 /* FIXME: placeholder value */
/* define this if the unit can be powered or charged via USB */
#define HAVE_USB_POWER
diff --git a/firmware/export/config/iriverifp7xx.h b/firmware/export/config/iriverifp7xx.h
deleted file mode 100644
index 0db9a7810d..0000000000
--- a/firmware/export/config/iriverifp7xx.h
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * This config file is for iriver iFP-799
- */
-
-#define IRIVER_IFP7XX_SERIES 1
-
-#define MODEL_NAME "iriver iFP-799"
-
-/* For Rolo and boot loader */
-#define MODEL_NUMBER 6
-
-/* define this if you have recording possibility */
-/*#define HAVE_RECORDING*/
-
-
-
-
-/* define this if you would like tagcache to build on this target */
-/* #define HAVE_TAGCACHE */
-
-/* LCD dimensions */
-#define LCD_WIDTH 128
-#define LCD_HEIGHT 64
-/* sqrt(128^2 + 64^2) / 1.0 = 143.1 */
-#define LCD_DPI 143
-#define LCD_DEPTH 1
-
-#define LCD_PIXELFORMAT VERTICAL_PACKING
-
-/* Display colours, for screenshots and sim (0xRRGGBB) */
-#define LCD_DARKCOLOR 0x000000
-#define LCD_BRIGHTCOLOR 0x5e6854
-#define LCD_BL_DARKCOLOR 0x000000
-#define LCD_BL_BRIGHTCOLOR 0x3ca0e6
-
-#define CONFIG_KEYPAD IRIVER_IFP7XX_PAD
-
-#define CONFIG_STORAGE STORAGE_NAND
-
-#define CONFIG_NAND NAND_IFP7XX
-
-#define HAVE_FAT16SUPPORT
-
-
-
-
-/* Define this if you have a software controlled poweroff */
-#define HAVE_SW_POWEROFF
-
-/* The number of bytes reserved for loadable codecs */
-#define CODEC_SIZE 0x38000
-
-/* The number of bytes reserved for loadable plugins */
-#define PLUGIN_BUFFER_SIZE 0x10000
-
-/* Define this if you have the WM8975 audio codec */
-/* #define HAVE_WM8975 */
-
-#define HAVE_LCD_CONTRAST
-
-#define MIN_CONTRAST_SETTING 5
-#define MAX_CONTRAST_SETTING 63
-#define DEFAULT_CONTRAST_SETTING 40
-
-/* define this if you have a flash memory storage */
-#define HAVE_FLASH_STORAGE
-
-#define BATTERY_CAPACITY_DEFAULT 1000 /* default battery capacity */
-#define BATTERY_CAPACITY_MIN 500 /* min. capacity selectable */
-#define BATTERY_CAPACITY_MAX 2800 /* max. capacity selectable */
-#define BATTERY_CAPACITY_INC 50 /* capacity increment */
-#define BATTERY_TYPES_COUNT 2 /* Alkalines or NiMH */
-
-#define CONFIG_BATTERY_MEASURE VOLTAGE_MEASURE
-
-/* define this if the unit should not shut down on low battery. */
-#define NO_LOW_BATTERY_SHUTDOWN
-
-/* Define this if you have a Philips PNX0101 */
-#define CONFIG_CPU PNX0101
-
-/* Define this if you want to use the PNX0101 i2c interface */
-#define CONFIG_I2C I2C_PNX0101
-
-/* The start address index for ROM builds */
-#define ROM_START 0x00000000
-
-/* Define this for LCD backlight available */
-#define HAVE_BACKLIGHT
-
-/* Define this to the CPU frequency */
-#define CPU_FREQ 48000000
-
-#define CONFIG_LCD LCD_IFP7XX
-
-/* Offset ( in the firmware file's header ) to the file length */
-#define FIRMWARE_OFFSET_FILE_LENGTH 0
-
-/* Offset ( in the firmware file's header ) to the file CRC */
-#define FIRMWARE_OFFSET_FILE_CRC 0
-
-/* Offset ( in the firmware file's header ) to the real data */
-#define FIRMWARE_OFFSET_FILE_DATA 8
-
-#define USB_ISP1582
-
-#define HAVE_GDB_API
-
-/* Define this if you have adjustable CPU frequency */
-#define HAVE_ADJUSTABLE_CPU_FREQ
-
-#define BOOTFILE_EXT "iriver"
-#define BOOTFILE "rockbox." BOOTFILE_EXT
-#define BOOTDIR "/.rockbox"
-
-#define IBSS_ATTR_VOICE_STACK
-#define ICODE_ATTR_TREMOR_NOT_MDCT
-#define ICODE_ATTR_TREMOR_MDCT
-#define ICODE_ATTR_FLAC
-#define IBSS_ATTR_FLAC_DECODED0
-#define ICONST_ATTR_MPA_HUFFMAN
-#define IBSS_ATTR_MPC_SAMPLE_BUF
-#define ICODE_ATTR_ALAC
-#define IBSS_ATTR_SHORTEN_DECODED0
-
-#define IRAM_LCDFRAMEBUFFER IBSS_ATTR /* put the lcd frame buffer in IRAM */
-
-/* Define this if a programmable hotkey is mapped */
-#define HAVE_HOTKEY
diff --git a/firmware/export/config/logikdax.h b/firmware/export/config/logikdax.h
deleted file mode 100644
index 47229f7a1d..0000000000
--- a/firmware/export/config/logikdax.h
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * This config file is for the Logik DAX MP3/DAB
- */
-
-/* For Rolo and boot loader */
-#define MODEL_NUMBER 33
-
-#define MODEL_NAME "Logik DAX MP3/DAB"
-
-/* define this if you have recording possibility */
-//#define HAVE_RECORDING
-
-/* Define bitmask of input sources - recordable bitmask can be defined
- explicitly if different */
-//#define INPUT_SRC_CAPS (SRC_CAP_MIC | SRC_CAP_LINEIN | SRC_CAP_SPDIF)
-
-#if 0 /* Enable for USB driver test */
-#define HAVE_USBSTACK
-#define USB_VENDOR_ID 0x13d1
-#define USB_PRODUCT_ID 0x1002
-#endif
-
-
-
-
-
-/* define this if you can flip your LCD */
-#define HAVE_LCD_FLIP
-
-/* define this if you can invert the colours on your LCD */
-#define HAVE_LCD_INVERT
-
-/* define this if you have access to the quickscreen */
-#define HAVE_QUICKSCREEN
-
-/* define this if you would like tagcache to build on this target */
-#define HAVE_TAGCACHE
-
-#define HAVE_FAT16SUPPORT
-
-/* define this if you have a flash memory storage */
-#define HAVE_FLASH_STORAGE
-
-#define CONFIG_STORAGE STORAGE_NAND
-
-#define CONFIG_NAND NAND_TCC
-
-/* LCD dimensions */
-#define LCD_WIDTH 128
-#define LCD_HEIGHT 64
-/* sqrt(128^2 + 64^2) / 1.0 = 143.1 */
-#define LCD_DPI 143
-#define LCD_DEPTH 1
-
-#define LCD_PIXELFORMAT VERTICAL_PACKING
-
-/* Display colours, for screenshots and sim (0xRRGGBB) */
-#define LCD_DARKCOLOR 0x000000
-#define LCD_BRIGHTCOLOR 0x5a915a
-#define LCD_BL_DARKCOLOR 0x000000
-#define LCD_BL_BRIGHTCOLOR 0x82b4fa
-
-/* define this to indicate your device's keypad */
-#define CONFIG_KEYPAD LOGIK_DAX_PAD
-
-/* define this if you have a real-time clock */
-#define CONFIG_RTC RTC_TCC77X
-
-/* define this if you have RTC RAM available for settings */
-//#define HAVE_RTC_RAM
-
-/* Define this if you have a software controlled poweroff */
-#define HAVE_SW_POWEROFF
-
-/* The number of bytes reserved for loadable codecs */
-#define CODEC_SIZE 0x38000
-
-/* The number of bytes reserved for loadable plugins */
-#define PLUGIN_BUFFER_SIZE 0x10000
-
-#define AB_REPEAT_ENABLE
-
-
-
-
-/* The DAX uses built-in WM8731 codec */
-#define HAVE_WM8731
-/* Codec is slave on serial bus */
-#define CODEC_SLAVE
-/* WM8731 has no tone controls, so we use the software ones */
-#define HAVE_SW_TONE_CONTROLS
-
-/* Define this for LCD backlight available */
-#define HAVE_BACKLIGHT
-
-#define CONFIG_I2C I2C_TCC77X
-
-#define BATTERY_CAPACITY_DEFAULT 1500 /* default battery capacity */
-#define BATTERY_CAPACITY_MIN 1500 /* min. capacity selectable */
-#define BATTERY_CAPACITY_MAX 3200 /* max. capacity selectable */
-#define BATTERY_CAPACITY_INC 50 /* capacity increment */
-#define BATTERY_TYPES_COUNT 1 /* only one type */
-
-#define CONFIG_BATTERY_MEASURE VOLTAGE_MEASURE
-
-/* define this if the unit should not shut down on low battery. */
-#define NO_LOW_BATTERY_SHUTDOWN
-
-/* Define this if you have a TCC773L */
-#define CONFIG_CPU TCC773L
-
-/* Define this if you have ATA power-off control */
-#define HAVE_ATA_POWER_OFF
-
-#define HAVE_FAT16SUPPORT
-
-/* Define this to the CPU frequency */
-#define CPU_FREQ 120000000
-
-/* Offset ( in the firmware file's header ) to the file length */
-#define FIRMWARE_OFFSET_FILE_LENGTH 0
-
-/* Offset ( in the firmware file's header ) to the file CRC */
-#define FIRMWARE_OFFSET_FILE_CRC 4
-
-/* Offset ( in the firmware file's header ) to the real data */
-#define FIRMWARE_OFFSET_FILE_DATA 6
-
-/* The start address index for ROM builds */
-/* #define ROM_START 0x11010 for behind original Archos */
-#define ROM_START 0x7010 /* for behind BootBox */
-
-#define CONFIG_LCD LCD_SSD1815
-
-#define BOOTFILE_EXT "logik"
-#define BOOTFILE "rockbox." BOOTFILE_EXT
-#define BOOTDIR "/"
-
-#define IBSS_ATTR_VOICE_STACK
-#define ICODE_ATTR_TREMOR_NOT_MDCT
-#define ICODE_ATTR_TREMOR_MDCT
-#define ICODE_ATTR_FLAC
-#define IBSS_ATTR_FLAC_DECODED0
-#define ICONST_ATTR_MPA_HUFFMAN
-#define IBSS_ATTR_MPC_SAMPLE_BUF
-#define ICODE_ATTR_ALAC
-#define IBSS_ATTR_SHORTEN_DECODED0
-
-#define IRAM_LCDFRAMEBUFFER IBSS_ATTR /* put the lcd frame buffer in IRAM */
-
-/* Define this if a programmable hotkey is mapped */
-//#define HAVE_HOTKEY
diff --git a/firmware/export/config/mrobe500.h b/firmware/export/config/mrobe500.h
index a7d72f76ac..621c0b75ca 100644
--- a/firmware/export/config/mrobe500.h
+++ b/firmware/export/config/mrobe500.h
@@ -65,9 +65,6 @@
/* define this if the target has volume keys which can be used in the lists */
#define HAVE_VOLUME_IN_LIST
-/* define this if you want viewport clipping enabled for safe LCD functions */
-#define HAVE_VIEWPORT_CLIP
-
/* LCD dimensions */
#define CONFIG_LCD LCD_MROBE500
@@ -198,7 +195,7 @@
/* define current usage levels */
#define CURRENT_NORMAL 85 /* Measured */
#define CURRENT_BACKLIGHT 200 /* Over 200 mA total measured when on */
-#define CURRENT_RECORD 0 /* no recording */
+#define CURRENT_REMOTE 2 /* FIXME: placeholder value */
/* Hardware controlled charging with monitoring */
#define CONFIG_CHARGING CHARGING_MONITOR
@@ -223,7 +220,7 @@
#define HAVE_USB_HID_MOUSE
/* Define this if hardware supports alternate blitting */
-#define HAVE_LCD_MODES LCD_MODE_RGB565 | LCD_MODE_YUV | LCD_MODE_PAL256
+#define HAVE_LCD_MODES (LCD_MODE_RGB565 | LCD_MODE_YUV | LCD_MODE_PAL256)
#define CONFIG_CPU DM320
diff --git a/firmware/export/config/nokian8xx.h b/firmware/export/config/nokian8xx.h
index 550ee112d1..5cdf42a11f 100644
--- a/firmware/export/config/nokian8xx.h
+++ b/firmware/export/config/nokian8xx.h
@@ -43,9 +43,6 @@
#define HAVE_TOUCHSCREEN
#define HAVE_BUTTON_DATA
-/* define this if you have RTC RAM available for settings */
-//#define HAVE_RTC_RAM
-
/* define this if you have a real-time clock */
#define CONFIG_RTC APPLICATION
diff --git a/firmware/export/config/nokian900.h b/firmware/export/config/nokian900.h
index ebbe5e2cc8..e7084f9acb 100644
--- a/firmware/export/config/nokian900.h
+++ b/firmware/export/config/nokian900.h
@@ -42,9 +42,6 @@
#define HAVE_TOUCHSCREEN
#define HAVE_BUTTON_DATA
-/* define this if you have RTC RAM available for settings */
-//#define HAVE_RTC_RAM
-
/* define this if you have a real-time clock */
#define CONFIG_RTC APPLICATION
diff --git a/firmware/export/config/pandora.h b/firmware/export/config/pandora.h
index 3b26ad9b7b..41b2b44dad 100644
--- a/firmware/export/config/pandora.h
+++ b/firmware/export/config/pandora.h
@@ -43,9 +43,6 @@
#define HAVE_TOUCHSCREEN
#define HAVE_BUTTON_DATA
-/* define this if you have RTC RAM available for settings */
-//#define HAVE_RTC_RAM
-
/* define this if you have a real-time clock */
#define CONFIG_RTC APPLICATION
diff --git a/firmware/export/config/samsungypr0.h b/firmware/export/config/samsungypr0.h
index ba5f02ad74..88ba96504d 100644
--- a/firmware/export/config/samsungypr0.h
+++ b/firmware/export/config/samsungypr0.h
@@ -68,10 +68,6 @@
/* TODO: ASCODEC has an auto dim feature, so disabling the supply to leds should do the trick. But for now I tested SW fading only */
#define CONFIG_BACKLIGHT_FADING BACKLIGHT_FADING_SW_SETTING
-/* define this if you have RTC RAM available for settings */
-/* TODO: in theory we could use that, ascodec offers us such a ram. we have also a small device, part of the nand of 1 MB size, that Samsung uses to store region code etc and it's almost unused space */
-//#define HAVE_RTC_RAM
-
/* define this if you have a real-time clock */
#define CONFIG_RTC RTC_AS3514
#define HAVE_RTC_ALARM
@@ -109,6 +105,8 @@
#define CONFIG_TUNER SI4700
#define HAVE_TUNER_PWR_CTRL
#define HAVE_RDS_CAP
+#define CONFIG_RDS (RDS_CFG_POLL | RDS_CFG_PROCESS)
+#define CONFIG_RDS_POLL_TICKS 4
/* Define this for FM radio input available */
#define HAVE_FMRADIO_IN
@@ -123,6 +121,7 @@
/* Define current usage levels. */
#define CURRENT_NORMAL 24 /* ~25h, on 600mAh that's about 24mA */
#define CURRENT_BACKLIGHT 62 /* ~6,5h -> 92mA. Minus 24mA normal that gives us 68mA */
+#define CURRENT_RECORD 2 /* FIXME: placeholder value */
#endif /* SIMULATOR */
diff --git a/firmware/export/config/samsungypr1.h b/firmware/export/config/samsungypr1.h
index d091e3ed1e..8ef76d60af 100644
--- a/firmware/export/config/samsungypr1.h
+++ b/firmware/export/config/samsungypr1.h
@@ -150,6 +150,8 @@
#define CONFIG_TUNER SI4700
#define HAVE_TUNER_PWR_CTRL
#define HAVE_RDS_CAP
+#define CONFIG_RDS (RDS_CFG_POLL | RDS_CFG_PROCESS)
+#define CONFIG_RDS_POLL_TICKS 4
/* Define this for FM radio input available */
#define HAVE_FMRADIO_IN
@@ -161,5 +163,6 @@
/* Define current usage levels. */
#define CURRENT_NORMAL 24 /* ~25h, on 600mAh that's about 24mA */
#define CURRENT_BACKLIGHT 62 /* ~6,5h -> 92mA. Minus 24mA normal that gives us 68mA */
+#define CURRENT_RECORD 2 /* FIXME: placeholder value */
#endif /* SIMULATOR */
diff --git a/firmware/export/config/samsungypz5.h b/firmware/export/config/samsungypz5.h
deleted file mode 100644
index 2af62c1cbe..0000000000
--- a/firmware/export/config/samsungypz5.h
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * This config file is for the Samsung YP-Z5
- */
-#define IMX233_SUBTARGET 3600
-#define IMX233_PACKAGE IMX233_BGA169
-#define IMX233_PARTITIONS IMX233_FREESCALE
-
-/* For Rolo and boot loader */
-#define MODEL_NUMBER 84
-#define MODEL_NAME "Samsung YP-Z5"
-/* Define if boot data from bootloader has been enabled for the target */
-#define HAVE_BOOTDATA
-
-#define HW_SAMPR_CAPS SAMPR_CAP_ALL_48
-
-/* define this if you have recording possibility */
-#define HAVE_RECORDING
-
-#define REC_SAMPR_CAPS SAMPR_CAP_ALL_48
-
-/* Default recording levels */
-#define DEFAULT_REC_MIC_GAIN 23
-#define DEFAULT_REC_LEFT_GAIN 23
-#define DEFAULT_REC_RIGHT_GAIN 23
-
-/* Define bitmask of input sources - recordable bitmask can be defined
- explicitly if different */
-#define HAVE_FMRADIO_IN
-#define INPUT_SRC_CAPS (SRC_CAP_MIC | SRC_CAP_FMRADIO)
-
-
-
-/* define this if you have a colour LCD */
-#define HAVE_LCD_COLOR
-
-#ifndef BOOTLOADER
-#define HAVE_ALBUMART
-
-/* define this to enable bitmap scaling */
-#define HAVE_BMP_SCALING
-
-/* define this to enable JPEG decoding */
-#define HAVE_JPEG
-
-/* Define this if a programmable hotkey is mapped */
-#define HAVE_HOTKEY
-
-/* define this if you have access to the quickscreen */
-#define HAVE_QUICKSCREEN
-
-/* define this if you would like tagcache to build on this target */
-#define HAVE_TAGCACHE
-
-/* define this if the target has volume keys which can be used in the lists */
-#define HAVE_VOLUME_IN_LIST
-
-/* define this if you have LCD enable function */
-//#define HAVE_LCD_ENABLE
-
-/* Define this if your LCD can be put to sleep. HAVE_LCD_ENABLE
- should be defined as well.
-#define HAVE_LCD_SLEEP
-#define HAVE_LCD_SLEEP_SETTING
-*/
-
-/* define this if you can flip your LCD
-#define HAVE_LCD_FLIP
-*/
-
-/* define this if you can invert the colours on your LCD
-#define HAVE_LCD_INVERT
-*/
-
-/* define this if you have a real-time clock */
-#define CONFIG_RTC RTC_IMX233
-
-/* define this if you have a real-time clock with alarm facilities */
-#define HAVE_RTC_ALARM
-
-#endif /* !BOOTLOADER */
-
-/* define this if you have an i.MX23 codec */
-#define HAVE_IMX233_CODEC
-
-#define CONFIG_TUNER TEA5767
-#define CONFIG_TUNER_XTAL 32768
-
-/* There is no hardware tone control */
-#define HAVE_SW_TONE_CONTROLS
-
-#define CONFIG_KEYPAD SAMSUNG_YPZ5_PAD
-#define HAVE_TOUCHPAD_IMX233
-
-/* Define this to enable morse code input */
-#define HAVE_MORSE_INPUT
-
-
-
-
-/* LCD dimensions */
-#define LCD_WIDTH 128
-#define LCD_HEIGHT 160
-/* sqrt(128^2 + 160^2) / 1.8 = 113.8 */
-#define LCD_DPI 114
-#define LCD_DEPTH 16 /* 65536 colours */
-#define LCD_PIXELFORMAT RGB565 /* rgb565 */
-
-/* Define this if you have a software controlled poweroff */
-#define HAVE_SW_POWEROFF
-
-/* Some devices seem to be FAT16 formatted */
-#define HAVE_FAT16SUPPORT
-
-/* The number of bytes reserved for loadable codecs */
-#define CODEC_SIZE 0x100000
-
-/* The number of bytes reserved for loadable plugins */
-#define PLUGIN_BUFFER_SIZE 0x80000
-
-#define AB_REPEAT_ENABLE
-
-/* Define this for LCD backlight available */
-#define HAVE_BACKLIGHT
-#define HAVE_BACKLIGHT_BRIGHTNESS
-
-/* Main LCD backlight brightness range and defaults */
-#define MIN_BRIGHTNESS_SETTING 1
-#define MAX_BRIGHTNESS_SETTING 18
-#define DEFAULT_BRIGHTNESS_SETTING 5
-
-/* Which backlight fading type? */
-#define CONFIG_BACKLIGHT_FADING BACKLIGHT_FADING_SW_SETTING
-
-/* define this if you have a flash memory storage */
-//#define HAVE_FLASH_STORAGE
-
-#define CONFIG_STORAGE (STORAGE_RAMDISK)
-//#define CONFIG_NAND NAND_IMX233
-//#define NUM_DRIVES 0
-
-/* Extra threads: touchpad and rds */
-//#define TARGET_EXTRA_THREADS 2
-
-/* todo */
-#define BATTERY_CAPACITY_DEFAULT 550 /* default battery capacity */
-#define BATTERY_CAPACITY_MIN 550 /* min. capacity selectable */
-#define BATTERY_CAPACITY_MAX 550 /* max. capacity selectable */
-#define BATTERY_CAPACITY_INC 0 /* capacity increment */
-#define BATTERY_TYPES_COUNT 1 /* only one type */
-
-#define CONFIG_BATTERY_MEASURE VOLTAGE_MEASURE
-
-/* Charging implemented in a target-specific algorithm */
-#define CONFIG_CHARGING CHARGING_TARGET
-
-/* define this if the unit can be powered or charged via USB */
-#define HAVE_USB_POWER
-
-/* Define this if you have an IMX233*/
-#define CONFIG_CPU IMX233
-
-/* Define this if you want to use the IMX233 i2c interface */
-#define CONFIG_I2C I2C_IMX233
-
-/* define current usage levels (based on battery bench) */
-#define CURRENT_NORMAL 35
-#define CURRENT_BACKLIGHT 30
-#define CURRENT_RECORD CURRENT_NORMAL
-
-/* maximum charging current */
-#define CURRENT_MAX_CHG 200
-
-/* Define this to the CPU frequency */
-#define CPU_FREQ 454000000
-
-/* Type of LCD */
-#define CONFIG_LCD LCD_SAMSUNGYPZ5
-
-/* Offset ( in the firmware file's header ) to the file CRC and data. These are
- only used when loading the old format rockbox.e200 file */
-#define FIRMWARE_OFFSET_FILE_CRC 0x0
-#define FIRMWARE_OFFSET_FILE_DATA 0x8
-
-/* USB On-the-go */
-#define CONFIG_USBOTG USBOTG_ARC
-
-/* enable these for the experimental usb stack */
-#define HAVE_USBSTACK
-#define USB_VENDOR_ID 0x04e8
-#define USB_PRODUCT_ID 0x5041
-#define HAVE_USB_HID_MOUSE
-#define HAVE_BOOTLOADER_USB_MODE
-
-/* Define this if you have adjustable CPU frequency */
-//#define HAVE_ADJUSTABLE_CPU_FREQ
-
-/* Virtual LED (icon) */
-#define CONFIG_LED LED_VIRTUAL
-
-#define BOOTFILE_EXT "samsung"
-#define BOOTFILE "rockbox." BOOTFILE_EXT
-#define BOOTDIR "/.rockbox"
-
-#define INCLUDE_TIMEOUT_API
diff --git a/firmware/export/config/sansac100.h b/firmware/export/config/sansac100.h
deleted file mode 100644
index c2956eed12..0000000000
--- a/firmware/export/config/sansac100.h
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * This config file is for the Sansa C100 series
- */
-
-#define MODEL_NAME "Sandisk Sansa c100 series"
-
-/* For Rolo and bootloader */
-#define MODEL_NUMBER 30
-
-/* define hardware samples rate caps mask */
-#define HW_SAMPR_CAPS (/*SAMPR_CAP_88 | */SAMPR_CAP_44/* | SAMPR_CAP_22 | SAMPR_CAP_11*/)
-
-
-
-
-/* define this if you have a colour LCD */
-#define HAVE_LCD_COLOR
-
-/* define this if you can flip your LCD */
-/*#define HAVE_LCD_FLIP*/
-
-/* define this if you can invert the colours on your LCD */
-/*#define HAVE_LCD_INVERT*/
-
-/* define this if you have access to the quickscreen */
-#define HAVE_QUICKSCREEN
-
-/* define this if you would like tagcache to build on this target */
-#define HAVE_TAGCACHE
-
-/* define this if you have a flash memory storage */
-#define HAVE_FLASH_STORAGE
-
-/* Only v1 */
-#define CONFIG_STORAGE STORAGE_NAND
-#define CONFIG_NAND NAND_TCC
-
-/* c100's with direct-to-NAND access are FAT16 */
-#define HAVE_FAT16SUPPORT
-
-/* LCD dimensions */
-#define LCD_WIDTH 128
-#define LCD_HEIGHT 64
-/* sqrt(128^2 + 64^2) / 1.7 = 84.2 */
-#define LCD_DPI 84
-#define LCD_DEPTH 16 /* 65536 colours */
-#define LCD_PIXELFORMAT RGB565 /*rgb565*/
-
-/*#define LCD_PIXELFORMAT VERTICAL_PACKING*/
-
-/* define this to indicate your device's keypad */
-#define CONFIG_KEYPAD SANSA_C100_PAD
-
-/* define this if you have a real-time clock */
-#define CONFIG_RTC RTC_TCC77X
-
-/* define this if you have RTC RAM available for settings */
-//#define HAVE_RTC_RAM
-
-/* Define this if you have a software controlled poweroff */
-#define HAVE_SW_POWEROFF
-
-/* The number of bytes reserved for loadable codecs */
-#define CODEC_SIZE 0x50000
-
-/* The number of bytes reserved for loadable plugins */
-#define PLUGIN_BUFFER_SIZE 0x50000
-
-#define AB_REPEAT_ENABLE
-
-
-
-
-/* Define this if you have the TLV320 audio codec */
-#define HAVE_TLV320
-
-/*#define CONFIG_TUNER TEA5767*/
-
-/* TLV320 has no tone controls, so we use the software ones */
-#define HAVE_SW_TONE_CONTROLS
-
-/* Define this for LCD backlight available */
-#define HAVE_BACKLIGHT
-
-#define CONFIG_I2C I2C_TCC77X
-
-#define BATTERY_CAPACITY_DEFAULT 540 /* default battery capacity */
-#define BATTERY_CAPACITY_MIN 540 /* min. capacity selectable */
-#define BATTERY_CAPACITY_MAX 540 /* max. capacity selectable */
-#define BATTERY_CAPACITY_INC 50 /* capacity increment */
-#define BATTERY_TYPES_COUNT 1 /* only one type */
-
-#define CONFIG_BATTERY_MEASURE VOLTAGE_MEASURE
-
-/* define this if the unit should not shut down on low battery. */
-#define NO_LOW_BATTERY_SHUTDOWN
-
-/* Define this if you have a TCC770 */
-#define CONFIG_CPU TCC770
-
-/* Define this if you have ATA power-off control */
-#define HAVE_ATA_POWER_OFF
-
-/* Define this to the CPU frequency */
-#define CPU_FREQ 120000000
-
-/* Offset ( in the firmware file's header ) to the file CRC */
-#define FIRMWARE_OFFSET_FILE_CRC 0
-
-/* Offset ( in the firmware file's header ) to the real data */
-#define FIRMWARE_OFFSET_FILE_DATA 8
-
-/* The start address index for ROM builds */
-/* #define ROM_START 0x11010 for behind original Archos */
-#define ROM_START 0x7010 /* for behind BootBox */
-
-#define CONFIG_LCD LCD_S6B33B2
-
-#define BOOTFILE_EXT "c100"
-#define BOOTFILE "rockbox." BOOTFILE_EXT
-#define BOOTDIR "/.rockbox"
-
-#ifdef BOOTLOADER
-#define TCCBOOT
-#endif
-
-#define IRAM_LCDFRAMEBUFFER IBSS_ATTR /* put the lcd frame buffer in IRAM */
-
-/* Define this if a programmable hotkey is mapped */
-#define HAVE_HOTKEY
diff --git a/firmware/export/config/sansaclipplus.h b/firmware/export/config/sansaclipplus.h
index 3108bace61..99eb1d8832 100644
--- a/firmware/export/config/sansaclipplus.h
+++ b/firmware/export/config/sansaclipplus.h
@@ -11,12 +11,16 @@
#define HAVE_BOOTDATA
/* define boot redirect file name allows booting from external drives */
#define BOOT_REDIR "rockbox_main.clip+"
+#define MULTIBOOT_MIN_VOLUME 1
#define HAVE_MULTIDRIVE
#define NUM_DRIVES 2
#ifndef BOOTLOADER
#define HAVE_HOTSWAP
+#define HAVE_RDS_CAP
+#define CONFIG_RDS (RDS_CFG_POLL | RDS_CFG_PROCESS)
+#define CONFIG_RDS_POLL_TICKS 4
#endif
#define HW_SAMPR_CAPS SAMPR_CAP_ALL_96
@@ -146,6 +150,9 @@
/* define this if the flash memory uses the SecureDigital Memory Card protocol */
#define CONFIG_STORAGE STORAGE_SD
+/* Define this if target has an additional number of threads specific to it */
+#define TARGET_EXTRA_THREADS 1 /* RDS thread */
+
#define BATTERY_CAPACITY_DEFAULT 290 /* default battery capacity */
#define BATTERY_CAPACITY_MIN 290 /* min. capacity selectable */
#define BATTERY_CAPACITY_MAX 290 /* max. capacity selectable */
@@ -195,6 +202,7 @@
#define HAVE_USBSTACK
#define USB_VENDOR_ID 0x0781
#define USB_PRODUCT_ID 0x74d1
+#define HAVE_USB_HID_MOUSE
#define HAVE_BOOTLOADER_USB_MODE
/* Virtual LED (icon) */
diff --git a/firmware/export/config/sansaclipzip.h b/firmware/export/config/sansaclipzip.h
index 6afe6e129e..d8b18e1a18 100644
--- a/firmware/export/config/sansaclipzip.h
+++ b/firmware/export/config/sansaclipzip.h
@@ -11,6 +11,7 @@
#define HAVE_BOOTDATA
/* define boot redirect file name allows booting from external drives */
#define BOOT_REDIR "rockbox_main.clipzip"
+#define MULTIBOOT_MIN_VOLUME 1
#define HAVE_MULTIDRIVE
#define NUM_DRIVES 2
diff --git a/firmware/export/config/sansaconnect.h b/firmware/export/config/sansaconnect.h
index 465a576664..a06ea9b207 100644
--- a/firmware/export/config/sansaconnect.h
+++ b/firmware/export/config/sansaconnect.h
@@ -66,9 +66,6 @@
/* define this if the target has volume keys which can be used in the lists */
#define HAVE_VOLUME_IN_LIST
-/* define this if you want viewport clipping enabled for safe LCD functions */
-#define HAVE_VIEWPORT_CLIP
-
/* LCD dimensions */
#define CONFIG_LCD LCD_CONNECT
@@ -81,9 +78,8 @@
#define LCD_PIXELFORMAT RGB565 /* rgb565 */
#define HAVE_LCD_ENABLE
-#ifndef BOOTLOADER
#define HAVE_LCD_SLEEP
-#endif
+#define HAVE_LCD_SHUTDOWN
#define LCD_SLEEP_TIMEOUT (2*HZ)
@@ -114,7 +110,7 @@
//#define HW_SAMPR_CAPS SAMPR_CAP_44 | SAMPR_CAP_22 | SAMPR_CAP_11 | SAMPR_CAP_8
/* define this if you have a real-time clock */
-//#define CONFIG_RTC RTC_STM41T62
+#define CONFIG_RTC RTC_CONNECT
/* define this if the unit uses a scrollwheel for navigation */
#define HAVE_SCROLLWHEEL
@@ -135,6 +131,12 @@
/* Define this if you have a software controlled poweroff */
#define HAVE_SW_POWEROFF
+#ifndef BOOTLOADER
+#define HAVE_WIFI
+/* define this if the target has Marvell 88W8686 interfaced over SPI */
+#define HAVE_W8686_SPI
+#endif
+
/* The number of bytes reserved for loadable codecs */
#define CODEC_SIZE 0x100000
@@ -147,7 +149,15 @@
#define BATTERY_CAPACITY_INC 100 /* capacity increment */
#define BATTERY_TYPES_COUNT 1 /* only one type */
-#define CONFIG_BATTERY_MEASURE VOLTAGE_MEASURE
+/* bq27000 provides voltage, percentage and time measure.
+ * Voltage reading is available every 2.56 seconds and does not need filtering.
+ * Read the measured voltage every 3 seconds so we are guaranteed to not read
+ * the same value twice (do not try to read every 2.56 seconds as clocks are
+ * not synchronized).
+ */
+#define CONFIG_BATTERY_MEASURE (VOLTAGE_MEASURE|PERCENTAGE_MEASURE|TIME_MEASURE)
+#define BATT_AVE_SAMPLES 1
+#define POWER_THREAD_STEP_TICKS (3*HZ)
/* define current usage levels */
#if 0
@@ -156,9 +166,6 @@
#define CURRENT_BACKLIGHT 200
#endif
-/* Hardware controlled charging with monitoring */
-//#define CONFIG_CHARGING CHARGING_MONITOR
-
#define CONFIG_CPU DM320
#define CONFIG_I2C I2C_DM320
@@ -182,11 +189,18 @@
/* Offset ( in the firmware file's header ) to the real data */
#define FIRMWARE_OFFSET_FILE_DATA 8
-#if 0
+/* Hardware controlled charging */
+#define CONFIG_CHARGING CHARGING_MONITOR
+
+#define CONFIG_USBOTG USBOTG_TNETV105
+
#define HAVE_USBSTACK
+#define HAVE_USB_POWER
+#define HAVE_USB_CHARGING_ENABLE
+#define HAVE_BOOTLOADER_USB_MODE
#define USB_VENDOR_ID 0x0781
#define USB_PRODUCT_ID 0x7480
-#endif
+#define USB_NUM_ENDPOINTS 5
#define INCLUDE_TIMEOUT_API
diff --git a/firmware/export/config/sansae200.h b/firmware/export/config/sansae200.h
index 59e531241f..6bb2df9bc8 100644
--- a/firmware/export/config/sansae200.h
+++ b/firmware/export/config/sansae200.h
@@ -13,6 +13,7 @@
/* define boot redirect file name allows booting from external drives */
#define BOOT_REDIR "rockbox_main.e200"
+#define MULTIBOOT_MIN_VOLUME 1
/* define this if you have recording possibility */
#define HAVE_RECORDING
diff --git a/firmware/export/config/sansafuze.h b/firmware/export/config/sansafuze.h
index b72f5b5198..e8f7157deb 100644
--- a/firmware/export/config/sansafuze.h
+++ b/firmware/export/config/sansafuze.h
@@ -10,6 +10,7 @@
/* define boot redirect file name allows booting from external drives */
#define BOOT_REDIR "rockbox_main.fuze"
+#define MULTIBOOT_MIN_VOLUME 1
#define HW_SAMPR_CAPS SAMPR_CAP_ALL_96
diff --git a/firmware/export/config/sansafuzeplus.h b/firmware/export/config/sansafuzeplus.h
index a853cb13b5..9b91e114bc 100644
--- a/firmware/export/config/sansafuzeplus.h
+++ b/firmware/export/config/sansafuzeplus.h
@@ -12,6 +12,7 @@
#define HAVE_BOOTDATA
/* define boot redirect file name allows booting from external drives */
#define BOOT_REDIR "rockbox_main.fuze+"
+#define MULTIBOOT_MIN_VOLUME 1
// HW can do it but we don't have the IRAM for mix buffers
//#define HW_SAMPR_CAPS SAMPR_CAP_ALL_192
diff --git a/firmware/export/config/sansafuzev2.h b/firmware/export/config/sansafuzev2.h
index fc2ff68257..96d3b2deee 100644
--- a/firmware/export/config/sansafuzev2.h
+++ b/firmware/export/config/sansafuzev2.h
@@ -10,6 +10,7 @@
/* define boot redirect file name allows booting from external drives */
#define BOOT_REDIR "rockbox_main.fuze2"
+#define MULTIBOOT_MIN_VOLUME 1
#define HW_SAMPR_CAPS SAMPR_CAP_ALL_96
diff --git a/firmware/export/config/sansam200.h b/firmware/export/config/sansam200.h
deleted file mode 100644
index 95647013e7..0000000000
--- a/firmware/export/config/sansam200.h
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * This config file is for the Sansa M200 series
- */
-
-/* For Rolo and boot loader */
-#define MODEL_NUMBER 29
-
-#define MODEL_NAME "Sandisk Sansa m200 series"
-
-/* Enable FAT16 support */
-#define HAVE_FAT16SUPPORT
-
-/* define this if you have recording possibility */
-//#define HAVE_RECORDING
-
-/* Define bitmask of input sources - recordable bitmask can be defined
- explicitly if different */
-//#define INPUT_SRC_CAPS (SRC_CAP_MIC | SRC_CAP_LINEIN | SRC_CAP_SPDIF)
-
-
-
-
-/* define this if you can flip your LCD */
-#define HAVE_LCD_FLIP
-
-/* define this if you can invert the colours on your LCD */
-#define HAVE_LCD_INVERT
-
-/* define this if you have access to the quickscreen */
-#define HAVE_QUICKSCREEN
-
-/* define this if you would like tagcache to build on this target */
-#define HAVE_TAGCACHE
-
-/* define this if you have a flash memory storage */
-#define HAVE_FLASH_STORAGE
-
-#define CONFIG_STORAGE STORAGE_NAND
-
-#define CONFIG_NAND NAND_TCC
-
-/* LCD dimensions */
-#define LCD_WIDTH 128
-#define LCD_HEIGHT 64
-/* sqrt(128^2 + 64^2) / 1.8 = 79.5 */
-#define LCD_DPI 80
-#define LCD_DEPTH 1
-
-#define LCD_PIXELFORMAT VERTICAL_PACKING
-
-/* Display colours, for screenshots and sim (0xRRGGBB) */
-#define LCD_BARKCOLOR 0x000000
-#define LCD_BRIGHTCOLOR 0x5e6854
-#define LCD_BL_DARKCOLOR 0x000000
-#define LCD_BL_BRIGHTCOLOR 0x3ca0e6
-
-/* define this to indicate your device's keypad */
-#define CONFIG_KEYPAD SANSA_M200_PAD
-
-/* Define this to enable morse code input */
-#define HAVE_MORSE_INPUT
-
-/* define this if you have a real-time clock */
-#define CONFIG_RTC RTC_TCC77X
-
-/* define this if you have RTC RAM available for settings */
-//#define HAVE_RTC_RAM
-
-/* Define this if you have a software controlled poweroff */
-#define HAVE_SW_POWEROFF
-
-/* The number of bytes reserved for loadable codecs */
-#define CODEC_SIZE 0x38000
-
-/* The number of bytes reserved for loadable plugins */
-#define PLUGIN_BUFFER_SIZE 0x10000
-
-#define AB_REPEAT_ENABLE
-
-
-
-
-/* Define this if you have the TLV320 audio codec */
-#define HAVE_TLV320
-
-/* TLV320 has no tone controls, so we use the software ones */
-#define HAVE_SW_TONE_CONTROLS
-
-/* Define this for LCD backlight available */
-#define HAVE_BACKLIGHT
-
-#define CONFIG_I2C I2C_TCC77X
-
-#define BATTERY_CAPACITY_DEFAULT 1500 /* default battery capacity */
-#define BATTERY_CAPACITY_MIN 1500 /* min. capacity selectable */
-#define BATTERY_CAPACITY_MAX 3200 /* max. capacity selectable */
-#define BATTERY_CAPACITY_INC 50 /* capacity increment */
-#define BATTERY_TYPES_COUNT 1 /* only one type */
-
-#define CONFIG_BATTERY_MEASURE VOLTAGE_MEASURE
-
-/* define this if the unit should not shut down on low battery. */
-#define NO_LOW_BATTERY_SHUTDOWN
-
-/* Define this if you have a TCC770 */
-#define CONFIG_CPU TCC770
-
-/* Define this if you have ATA power-off control */
-#define HAVE_ATA_POWER_OFF
-
-/* Define this to the CPU frequency */
-#define CPU_FREQ 120000000
-
-/* Offset ( in the firmware file's header ) to the file CRC */
-#define FIRMWARE_OFFSET_FILE_CRC 0
-
-/* Offset ( in the firmware file's header ) to the real data */
-#define FIRMWARE_OFFSET_FILE_DATA 8
-
-#define CONFIG_LCD LCD_SSD1815
-
-#define BOOTFILE_EXT "m200"
-#define BOOTFILE "rockbox." BOOTFILE_EXT
-#define BOOTDIR "/"
-
-#define IBSS_ATTR_VOICE_STACK
-#define ICODE_ATTR_TREMOR_NOT_MDCT
-#define ICODE_ATTR_TREMOR_MDCT
-#define ICODE_ATTR_FLAC
-#define IBSS_ATTR_FLAC_DECODED0
-#define ICONST_ATTR_MPA_HUFFMAN
-#define IBSS_ATTR_MPC_SAMPLE_BUF
-#define ICODE_ATTR_ALAC
-#define IBSS_ATTR_SHORTEN_DECODED0
-
-#define IRAM_LCDFRAMEBUFFER IBSS_ATTR /* put the lcd frame buffer in IRAM */
-
-/* Define this if a programmable hotkey is mapped */
-#define HAVE_HOTKEY
diff --git a/firmware/export/config/sansam200v4.h b/firmware/export/config/sansam200v4.h
index 97462dc6e2..37f0931079 100644
--- a/firmware/export/config/sansam200v4.h
+++ b/firmware/export/config/sansam200v4.h
@@ -75,9 +75,6 @@
#define CONFIG_RTC RTC_AS3514
#endif
-/* define this if you have RTC RAM available for settings */
-//#define HAVE_RTC_RAM
-
/* Define this if you have a software controlled poweroff */
#define HAVE_SW_POWEROFF
diff --git a/firmware/export/config/sdlapp.h b/firmware/export/config/sdlapp.h
index d8c1266f51..3286e377ce 100644
--- a/firmware/export/config/sdlapp.h
+++ b/firmware/export/config/sdlapp.h
@@ -52,9 +52,6 @@
#define HAVE_TOUCHSCREEN
#define HAVE_BUTTON_DATA
-/* define this if you have RTC RAM available for settings */
-//#define HAVE_RTC_RAM
-
/* define this if you have a real-time clock */
#define CONFIG_RTC APPLICATION
diff --git a/firmware/export/config/shanlingq1.h b/firmware/export/config/shanlingq1.h
new file mode 100644
index 0000000000..6f5365a97e
--- /dev/null
+++ b/firmware/export/config/shanlingq1.h
@@ -0,0 +1,136 @@
+/* RoLo-related defines */
+#define MODEL_NAME "Shanling Q1"
+#define MODEL_NUMBER 115
+#define BOOTFILE_EXT "q1"
+#define BOOTFILE "rockbox." BOOTFILE_EXT
+#define BOOTDIR "/.rockbox"
+#define FIRMWARE_OFFSET_FILE_CRC 0
+#define FIRMWARE_OFFSET_FILE_DATA 8
+
+/* CPU defines */
+#define CONFIG_CPU X1000
+#define X1000_EXCLK_FREQ 24000000
+#define CPU_FREQ 1008000000
+
+#ifndef SIMULATOR
+#define TIMER_FREQ X1000_EXCLK_FREQ
+#endif
+
+/* Kernel defines */
+#define INCLUDE_TIMEOUT_API
+#define HAVE_SEMAPHORE_OBJECTS
+
+/* Drivers */
+#define HAVE_I2C_ASYNC
+
+/* Buffer for plugins and codecs. */
+#define PLUGIN_BUFFER_SIZE 0x200000 /* 2 MiB */
+#define CODEC_SIZE 0x100000 /* 1 MiB */
+
+/* LCD defines */
+#define CONFIG_LCD LCD_SHANLING_Q1
+#define LCD_WIDTH 360
+#define LCD_HEIGHT 400
+#define LCD_DEPTH 16
+#define LCD_PIXELFORMAT RGB565
+#define LCD_DPI 200
+#define HAVE_LCD_COLOR
+#define HAVE_LCD_BITMAP
+#define HAVE_LCD_ENABLE
+#define LCD_X1000_FASTSLEEP
+#define LCD_X1000_DMA_WAIT_FOR_FRAME
+
+/* Backlight defines */
+#define HAVE_BACKLIGHT
+#define HAVE_BACKLIGHT_BRIGHTNESS
+#define MIN_BRIGHTNESS_SETTING 1
+#define MAX_BRIGHTNESS_SETTING 100
+#define BRIGHTNESS_STEP 5
+#define DEFAULT_BRIGHTNESS_SETTING 70
+#define CONFIG_BACKLIGHT_FADING BACKLIGHT_FADING_SW_SETTING
+
+/* Codec / audio hardware defines */
+#define HW_SAMPR_CAPS SAMPR_CAP_ALL_192
+#define HAVE_ES9218
+#define HAVE_SW_TONE_CONTROLS
+
+/* Button defines */
+#define CONFIG_KEYPAD SHANLING_Q1_PAD
+#define HAVE_TOUCHSCREEN
+#define HAVE_BUTTON_DATA
+#define HAVE_FT6x06
+#define FT6x06_NUM_POINTS 5
+#define HAVE_HEADPHONE_DETECTION
+
+/* Storage defines */
+#define CONFIG_STORAGE STORAGE_SD
+#define HAVE_HOTSWAP
+#define HAVE_HOTSWAP_STORAGE_AS_MAIN
+#define HAVE_MULTIDRIVE
+#define HAVE_MULTIVOLUME
+#define NUM_DRIVES 1
+#define STORAGE_WANTS_ALIGN
+#define STORAGE_NEEDS_BOUNCE_BUFFER
+
+/* RTC settings */
+#define CONFIG_RTC RTC_X1000
+/* TODO: implement HAVE_RTC_ALARM */
+
+/* Power management */
+#define CONFIG_BATTERY_MEASURE (VOLTAGE_MEASURE|CURRENT_MEASURE)
+#define CONFIG_CHARGING CHARGING_MONITOR
+#define HAVE_SW_POWEROFF
+
+#ifndef SIMULATOR
+/* TODO: get the CW2015 driver working correctly */
+/* #define HAVE_CW2015 */
+#define HAVE_AXP_PMU 192 /* Presumed */
+#define HAVE_POWEROFF_WHILE_CHARGING
+#endif
+
+/* Only one battery type */
+#define BATTERY_CAPACITY_DEFAULT 1100
+#define BATTERY_CAPACITY_MIN 1100
+#define BATTERY_CAPACITY_MAX 1100
+#define BATTERY_CAPACITY_INC 0
+#define BATTERY_TYPES_COUNT 1
+
+/* Multiboot */
+#define HAVE_BOOTDATA
+#define BOOT_REDIR "rockbox_main.shanling_q1"
+
+/* USB support */
+#ifndef SIMULATOR
+#define CONFIG_USBOTG USBOTG_DESIGNWARE
+#define USB_DW_TURNAROUND 5
+#define HAVE_USBSTACK
+#define USB_VENDOR_ID 0x0525 /* Same as the xDuuo X3, for some reason. */
+#define USB_PRODUCT_ID 0xa4a5 /* Nb. DAC mode uses 20b1:301f 'XMOS Ltd' */
+#define USB_DEVBSS_ATTR __attribute__((aligned(32)))
+#define HAVE_USB_POWER
+#define HAVE_USB_CHARGING_ENABLE
+#define HAVE_USB_CHARGING_IN_THREAD
+#define TARGET_USB_CHARGING_DEFAULT USB_CHARGING_FORCE
+#define HAVE_BOOTLOADER_USB_MODE
+/* This appears to improve transfer performance (the default is 64 KiB).
+ * Going any higher doesn't help but we're still slower than the OF. */
+#define USB_READ_BUFFER_SIZE (128 * 1024)
+#define USB_WRITE_BUFFER_SIZE (128 * 1024)
+#endif
+
+#ifdef BOOTLOADER
+/* Ignore on any key can cause surprising USB issues in the bootloader */
+# define USBPOWER_BTN_IGNORE (~(BUTTON_PREV|BUTTON_NEXT))
+#endif
+
+/* Rockbox capabilities */
+#define HAVE_FAT16SUPPORT
+#define HAVE_ALBUMART
+#define HAVE_BMP_SCALING
+#define HAVE_JPEG
+#define HAVE_TAGCACHE
+#define HAVE_VOLUME_IN_LIST
+#define HAVE_QUICKSCREEN
+#define HAVE_HOTKEY
+#define AB_REPEAT_ENABLE
+#define HAVE_BOOTLOADER_SCREENDUMP
diff --git a/firmware/export/config/tatungtpj1022.h b/firmware/export/config/tatungtpj1022.h
deleted file mode 100644
index 3865e8f87f..0000000000
--- a/firmware/export/config/tatungtpj1022.h
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * This config file is for the Tatung Elio TPJ-1022
- */
-
-#define MODEL_NAME "Tatung Elio TPJ-1022"
-
-/* For Rolo and boot loader */
-#define MODEL_NUMBER 15
-
-/* define this if you use an ATA controller */
-#define CONFIG_STORAGE STORAGE_ATA
-
-/*define this if the ATA controller and method of USB access support LBA48 */
-#define HAVE_LBA48
-
-/* define this if you have recording possibility */
-/*#define HAVE_RECORDING*/ /* TODO: add support for this */
-
-/* define the bitmask of hardware sample rates */
-#define HW_SAMPR_CAPS (SAMPR_CAP_96 | SAMPR_CAP_88 | SAMPR_CAP_48 | \
- SAMPR_CAP_44 | SAMPR_CAP_32 | SAMPR_CAP_8)
-
-/* define the bitmask of recording sample rates
-#define REC_SAMPR_CAPS (SAMPR_CAP_96 | SAMPR_CAP_88 | SAMPR_CAP_48 | \
- SAMPR_CAP_44 | SAMPR_CAP_32 | SAMPR_CAP_8) */
-
-
-
-
-/* define this if you have a colour LCD */
-#define HAVE_LCD_COLOR
-
-/* define this if you have access to the quickscreen */
-#define HAVE_QUICKSCREEN
-
-/* LCD dimensions */
-#define LCD_WIDTH 220
-#define LCD_HEIGHT 176
-/* sqrt(220^2 + 176^2) / 2.2 = 128.1 */
-#define LCD_DPI 128
-#define LCD_DEPTH 16 /* 65536 colours */
-#define LCD_PIXELFORMAT RGB565
-
-/* #define IRAM_LCDFRAMEBUFFER IDATA_ATTR *//* put the lcd frame buffer in IRAM */
-
-#define CONFIG_KEYPAD TATUNG_TPJ1022_PAD
-
-
-
-
-/* define this if you have a real-time clock */
-#ifndef BOOTLOADER
-//#define CONFIG_RTC RTC_E8564
-#endif
-
-/* Define this if you have a software controlled poweroff */
-#define HAVE_SW_POWEROFF
-
-/* The number of bytes reserved for loadable codecs */
-#define CODEC_SIZE 0x100000
-
-/* The number of bytes reserved for loadable plugins */
-#define PLUGIN_BUFFER_SIZE 0x80000
-
-/* Define this if you have the WM8731 audio codec */
-#define HAVE_WM8731
-
-#define AB_REPEAT_ENABLE
-
-/* define this if you have a disk storage, i.e. something
- that needs spinups and can cause skips when shaked */
-#define HAVE_DISK_STORAGE
-
-/* Define this for LCD backlight available */
-#define HAVE_BACKLIGHT
-
-#define BATTERY_CAPACITY_DEFAULT 1550 /* default battery capacity
- TODO: check this, probably different
- for different models too */
-#define BATTERY_CAPACITY_MIN 1500 /* min. capacity selectable */
-#define BATTERY_CAPACITY_MAX 1600 /* max. capacity selectable */
-#define BATTERY_CAPACITY_INC 10 /* capacity increment */
-#define BATTERY_TYPES_COUNT 1 /* only one type */
-
-#define CONFIG_BATTERY_MEASURE VOLTAGE_MEASURE
-
-/* Hardware controlled charging? FIXME */
-#define CONFIG_CHARGING CHARGING_SIMPLE
-
-/* define this if the unit can be powered or charged via USB */
-/*#define HAVE_USB_POWER*/
-
-/* Define this if you have a PortalPlayer PP5020 */
-#define CONFIG_CPU PP5020
-
-/* Define this if you want to use the PP5020 i2c interface */
-#define CONFIG_I2C I2C_PP5020
-
-/* define this if the hardware can be powered off while charging */
-/* TODO: should this be set for the H10? */
-//#define HAVE_POWEROFF_WHILE_CHARGING
-
-/* The start address index for ROM builds */
-#define ROM_START 0x00000000
-
-/* Define this to the CPU frequency */
-/* TODO: this is probably wrong */
-#define CPU_FREQ 11289600
-
-/* Type of LCD */
-#define CONFIG_LCD LCD_TPJ1022
-
-#define DEFAULT_CONTRAST_SETTING 19
-
-/* Offset ( in the firmware file's header ) to the file length */
-#define FIRMWARE_OFFSET_FILE_LENGTH 0
-
-/* Offset ( in the firmware file's header ) to the file CRC */
-#define FIRMWARE_OFFSET_FILE_CRC 0
-
-/* Offset ( in the firmware file's header ) to the real data */
-#define FIRMWARE_OFFSET_FILE_DATA 8
-
-/* USB On-the-go */
-#define CONFIG_USBOTG USBOTG_ARC
-
-/* define this if the unit can be powered or charged via USB */
-#define HAVE_USB_POWER
-
-/* enable these for the experimental usb stack ROOLKU */
-#define HAVE_USBSTACK
-#define USB_VENDOR_ID 0x07B4
-#define USB_PRODUCT_ID 0x0280
-
-/* Define this if you have adjustable CPU frequency */
-#define HAVE_ADJUSTABLE_CPU_FREQ
-
-#define BOOTFILE_EXT "elio"
-#define BOOTFILE "rockbox." BOOTFILE_EXT
-#define BOOTDIR "/.rockbox"
-
-
-/* DMA is used only for reading on PP502x because although reads are ~8x faster
- * writes appear to be ~25% slower.
- */
-#ifndef BOOTLOADER
-#define HAVE_ATA_DMA
-#endif
-
-/* Define this if a programmable hotkey is mapped */
-//#define HAVE_HOTKEY
diff --git a/firmware/export/config/xduoox3.h b/firmware/export/config/xduoox3.h
index 6cc1aa2e57..ea86e60d59 100644
--- a/firmware/export/config/xduoox3.h
+++ b/firmware/export/config/xduoox3.h
@@ -185,7 +185,7 @@
/* enable these for the experimental usb stack */
#define HAVE_USBSTACK
-#define HAVE_BOOTLOADER_USB_MODE
+//#define HAVE_BOOTLOADER_USB_MODE
/* Connect by events, not by tick polling */
#define USB_STATUS_BY_EVENT
diff --git a/firmware/export/cpu.h b/firmware/export/cpu.h
index aade199dd2..67509141aa 100644
--- a/firmware/export/cpu.h
+++ b/firmware/export/cpu.h
@@ -50,9 +50,6 @@
#if CONFIG_CPU == IMX31L
#include "imx31l.h"
#endif
-#ifdef CPU_TCC77X
-#include "tcc77x.h"
-#endif
#ifdef CPU_TCC780X
#include "tcc780x.h"
#endif
@@ -80,3 +77,6 @@
#if CONFIG_CPU == RK27XX
#include "rk27xx.h"
#endif
+#if CONFIG_CPU == X1000
+#include "x1000.h"
+#endif
diff --git a/firmware/export/cw2015.h b/firmware/export/cw2015.h
new file mode 100644
index 0000000000..c810d1b7b5
--- /dev/null
+++ b/firmware/export/cw2015.h
@@ -0,0 +1,57 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef __CW2015_H__
+#define __CW2015_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+
+/* Driver for I2C battery fuel gauge IC CW2015. */
+
+#define CW2015_REG_VERSION 0x00
+#define CW2015_REG_VCELL 0x02 /* 14 bits, registers 0x02 - 0x03 */
+#define CW2015_REG_SOC 0x04 /* 16 bits, registers 0x04 - 0x05 */
+#define CW2015_REG_RRT_ALERT 0x06 /* 13 bit RRT + alert flag, 0x06-0x07 */
+#define CW2015_REG_CONFIG 0x08
+#define CW2015_REG_MODE 0x0a
+#define CW2015_REG_BATINFO 0x10 /* cf. mainline Linux CW2015 driver */
+#define CW2015_SIZE_BATINFO 64
+
+extern void cw2015_init(void);
+
+/* Read the battery terminal voltage, converted to millivolts. */
+extern int cw2015_get_vcell(void);
+
+/* Read the SOC, in percent (0-100%). */
+extern int cw2015_get_soc(void);
+
+/* Get the estimated remaining run time, in minutes.
+ * Not a linearly varying quantity, according to the datasheet. */
+extern int cw2015_get_rrt(void);
+
+/* Read the current battery profile */
+extern const uint8_t* cw2015_get_bat_info(void);
+
+/* Debug screen */
+extern bool cw2015_debug_menu(void);
+
+#endif /* __CW2015_H__ */
diff --git a/firmware/export/disk.h b/firmware/export/disk.h
index c66028fe45..e465552fdc 100644
--- a/firmware/export/disk.h
+++ b/firmware/export/disk.h
@@ -35,6 +35,9 @@ struct partinfo
#define PARTITION_TYPE_FAT32_LBA 0x0c
#define PARTITION_TYPE_FAT16 0x06
#define PARTITION_TYPE_OS2_HIDDEN_C_DRIVE 0x84
+#define PARTITION_TYPE_GPT_GUARD 0xee
+
+#define MAX_PARTITIONS_PER_DRIVE 4 /* Needs to be at least 4 */
bool disk_init(IF_MD_NONVOID(int drive));
bool disk_partinfo(int partition, struct partinfo *info);
diff --git a/firmware/export/dm320.h b/firmware/export/dm320.h
index def8508b0b..bd6ca15407 100644
--- a/firmware/export/dm320.h
+++ b/firmware/export/dm320.h
@@ -30,13 +30,17 @@
#if !defined(__ASSEMBLER__) && !defined(__LD__)
/* These variables are created during linking (app/boot.lds) */
extern unsigned long _lcdbuf;
+#ifdef MROBE_500
extern unsigned long _lcdbuf2;
+#endif
extern unsigned long _ttbstart;
#endif
#define TTB_BASE_ADDR (_ttbstart) /* End of memory */
#define FRAME ((short *) (&_lcdbuf)) /* Right after TTB */
+#ifdef MROBE_500
#define FRAME2 ((short *) (&_lcdbuf2)) /* Right after FRAME */
+#endif
#define PHY_IO_BASE 0x00030000
#define DM320_REG(addr) (*(volatile unsigned short *)(PHY_IO_BASE + (addr)))
@@ -930,11 +934,27 @@ extern unsigned long _ttbstart;
#define MMC_ST1_DXFULL (1 << 3)
#define MMC_ST1_DAT3ST (1 << 4)
+#define MMC_IE_EDATDNE (1 << 0)
+#define MMC_IE_EBSYDNE (1 << 1)
+#define MMC_IE_ERSPDNE (1 << 2)
+#define MMC_IE_ETOUTRD (1 << 3)
+#define MMC_IE_ETOUTRS (1 << 4)
+#define MMC_IE_ECRCWR (1 << 5)
+#define MMC_IE_ECRCRD (1 << 6)
+#define MMC_IE_ECRCRS (1 << 7)
+#define MMC_IE_EDXRDY (1 << 9)
+#define MMC_IE_EDRRDY (1 << 10)
+#define MMC_IE_EDATED (1 << 11)
+
#define MMC_DMAMODE_RD_WORDSWAP (1 << 10)
#define MMC_DMAMODE_WR_WORDSWAP (1 << 11)
#define MMC_DMAMODE_WRITE (1 << 12)
#define MMC_DMAMODE_ENABLE (1 << 13)
#define MMC_DMAMODE_TIMEOUTIRQ_EN (1 << 14)
+
+#define MMC_DMASTAT1_RUNST (1 << 12)
+#define MMC_DMASTAT1_TOUTDT (1 << 13)
+
/*
* IO_EINTx bits
*/
@@ -1001,6 +1021,7 @@ extern unsigned long _ttbstart;
#define INTR_IRQ1_EXT0 INTR_EINT1_EXT0
#define INTR_IRQ1_EXT2 INTR_EINT1_EXT2
#define INTR_IRQ1_EXT7 INTR_EINT1_EXT7
+#define INTR_IRQ1_MMCSDMS0 INTR_EINT1_MMCSDMS0
#define INTR_IRQ1_MTC0 INTR_EINT1_MTC0
/*
diff --git a/firmware/export/eros_qn_codec.h b/firmware/export/eros_qn_codec.h
new file mode 100644
index 0000000000..223ef06779
--- /dev/null
+++ b/firmware/export/eros_qn_codec.h
@@ -0,0 +1,56 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ *
+ * Copyright (c) 2021 Andrew Ryabinin, Dana Conrad
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef _EROS_QN_CODEC_H
+#define _EROS_QN_CODEC_H
+
+/*
+ * Note: Maximum volume is set one step below unity in order to
+ * avoid overflowing pcm samples due to our DC Offset.
+ *
+ * The DAC's output is hot enough this should not be an issue.
+ */
+#define PCM5102A_VOLUME_MIN -740
+#define PCM5102A_VOLUME_MAX -20
+
+/* a small DC offset prevents play/pause clicking due to the PCM5102A DAC auto-muting */
+#define PCM_DC_OFFSET_VALUE -1
+
+AUDIOHW_SETTING(VOLUME, "dB", 0, 2, PCM5102A_VOLUME_MIN/10, PCM5102A_VOLUME_MAX/10, 0)
+
+/* flag indicating whether this is a new revision unit with the ES9018K2M DAC */
+extern int es9018k2m_present_flag;
+
+/* Switch the output sink. 0 - headphones, 1 - line out */
+void eros_qn_switch_output(int select);
+
+/* Record last volume setting for switching between headphones/line out */
+void eros_qn_set_last_vol(long int vol_l, long int vol_r);
+
+/* this just calls audiohw_set_volume() with the last (locally) known volume,
+ * used for switching to/from fixed line out volume. */
+void eros_qn_set_outputs(void);
+
+/* returns (global_settings.volume_limit * 10) */
+int eros_qn_get_volume_limit(void);
+
+#endif
diff --git a/firmware/export/erosqlinux_codec.h b/firmware/export/erosqlinux_codec.h
index c337bb78c7..ecc10be924 100644
--- a/firmware/export/erosqlinux_codec.h
+++ b/firmware/export/erosqlinux_codec.h
@@ -3,7 +3,16 @@
#define AUDIOHW_CAPS (LINEOUT_CAP)
-AUDIOHW_SETTING(VOLUME, "dB", 0, 1, -43, 0, -20)
+/* a small DC offset prevents play/pause clicking due to the DAC auto-muting */
+#define PCM_DC_OFFSET_VALUE -1
+
+/*
+ * Note: Maximum volume is set one step below unity in order to
+ * avoid overflowing pcm samples due to our DC Offset.
+ *
+ * The DAC's output is hot enough this should not be an issue.
+ */
+AUDIOHW_SETTING(VOLUME, "dB", 0, 2, -74, -2, -40)
//#define AUDIOHW_NEEDS_INITIAL_UNMUTE
diff --git a/firmware/export/es9018k2m.h b/firmware/export/es9018k2m.h
new file mode 100644
index 0000000000..2824fed505
--- /dev/null
+++ b/firmware/export/es9018k2m.h
@@ -0,0 +1,74 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ *
+ * Copyright (c) 2023 Dana Conrad
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef _ES9018K2M_H
+#define _ES9018K2M_H
+
+//======================================================================================
+// ES9018K2M support stuff
+// Implement audiohw_* functions in audiohw-*.c. These functions are utilities which
+// may be used there.
+
+// AUDIOHW_SETTING(VOLUME, *) not set here, probably best to put it in device-specific *_codec.h
+#ifdef AUDIOHW_HAVE_SHORT_ROLL_OFF
+AUDIOHW_SETTING(FILTER_ROLL_OFF, "", 0, 1, 0, 3, 0)
+#endif
+
+#ifndef ES9018K2M_VOLUME_MIN
+# define ES9018K2M_VOLUME_MIN -1270
+#endif
+
+#ifndef ES9018K2M_VOLUME_MAX
+# define ES9018K2M_VOLUME_MAX 0
+#endif
+
+#define ES9018K2M_REG0_SYSTEM_SETTINGS 0
+#define ES9018K2M_REG1_INPUT_CONFIG 1
+#define ES9018K2M_REG4_AUTOMUTE_TIME 4
+#define ES9018K2M_REG5_AUTOMUTE_LEVEL 5
+#define ES9018K2M_REG6_DEEMPHASIS 6
+#define ES9018K2M_REG7_GENERAL_SETTINGS 7
+#define ES9018K2M_REG8_GPIO_CONFIG 8
+#define ES9018K2M_REG10_MASTER_MODE_CTRL 10
+#define ES9018K2M_REG11_CHANNEL_MAPPING 11
+#define ES9018K2M_REG12_DPLL_SETTINGS 12
+#define ES9018K2M_REG13_THD_COMP 13
+#define ES9018K2M_REG14_SOFTSTART_SETTINGS 14
+#define ES9018K2M_REG15_VOLUME_L 15
+#define ES9018K2M_REG16_VOLUME_R 16
+#define ES9018K2M_REG21_GPIO_INPUT_SELECT 21
+
+/* writes volume levels to DAC over I2C, asynchronously */
+void es9018k2m_set_volume_async(int vol_l, int vol_r);
+
+/* write filter roll-off setting to DAC over I2C, synchronously */
+void es9018k2m_set_filter_roll_off(int value);
+
+/* writes a single register */
+/* returns I2C_STATUS_OK upon success, I2C_STATUS_* errors upon error */
+int es9018k2m_write_reg(uint8_t reg, uint8_t val);
+
+/* reads a single register */
+/* returns register value, or -1 upon error */
+int es9018k2m_read_reg(uint8_t reg);
+
+#endif \ No newline at end of file
diff --git a/firmware/export/es9218.h b/firmware/export/es9218.h
new file mode 100644
index 0000000000..1492304c67
--- /dev/null
+++ b/firmware/export/es9218.h
@@ -0,0 +1,230 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef __ES9218_H__
+#define __ES9218_H__
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#define AUDIOHW_CAPS (FILTER_ROLL_OFF_CAP|POWER_MODE_CAP)
+#define AUDIOHW_HAVE_ES9218_ROLL_OFF
+
+#define ES9218_DIG_VOLUME_MIN (-1275)
+#define ES9218_DIG_VOLUME_MAX 0
+#define ES9218_DIG_VOLUME_STEP 5
+
+#define ES9218_AMP_VOLUME_MIN (-240)
+#define ES9218_AMP_VOLUME_MAX 0
+#define ES9218_AMP_VOLUME_STEP 10
+
+AUDIOHW_SETTING(VOLUME, "dB", 1, ES9218_DIG_VOLUME_STEP,
+ ES9218_DIG_VOLUME_MIN, ES9218_DIG_VOLUME_MAX, -200)
+AUDIOHW_SETTING(FILTER_ROLL_OFF, "", 0, 1, 0, 7, 0)
+AUDIOHW_SETTING(POWER_MODE, "", 0, 1, 0, 1, 0)
+
+/* Register addresses */
+#define ES9218_REG_SYSTEM 0x00
+#define ES9218_REG_INPUT_SEL 0x01
+#define ES9218_REG_MIX_AUTOMUTE 0x02
+#define ES9218_REG_ANALOG_VOL 0x03
+#define ES9218_REG_AUTOMUTE_TIME 0x04
+#define ES9218_REG_AUTOMUTE_LEVEL 0x05
+#define ES9218_REG_DOP_VOLUME_RAMP 0x06
+#define ES9218_REG_FILTER_SYS_MUTE 0x07
+#define ES9218_REG_GPIO1_2_CONFIG 0x08
+#define ES9218_REG_RESERVED_1 0x09
+#define ES9218_REG_MASTER_MODE_CONFIG 0x0a
+#define ES9218_REG_OVERCURRENT_PROT 0x0b
+#define ES9218_REG_ASRC_DPLL_BANDWIDTH 0x0c
+#define ES9218_REG_THD_COMP_BYPASS 0x0d
+#define ES9218_REG_SOFT_START_CONFIG 0x0e
+#define ES9218_REG_VOLUME_LEFT 0x0f
+#define ES9218_REG_VOLUME_RIGHT 0x10
+#define ES9218_REG_MASTER_TRIM_BIT0_7 0x11
+#define ES9218_REG_MASTER_TRIM_BIT8_15 0x12
+#define ES9218_REG_MASTER_TRIM_BIT16_23 0x13
+#define ES9218_REG_MASTER_TRIM_BIT24_31 0x14
+#define ES9218_REG_GPIO_INPUT_SEL 0x15
+#define ES9218_REG_THD_COMP_C2_LO 0x16
+#define ES9218_REG_THD_COMP_C2_HI 0x17
+#define ES9218_REG_THD_COMP_C3_LO 0x18
+#define ES9218_REG_THD_COMP_C3_HI 0x19
+#define ES9218_REG_CHARGE_PUMP_SS_DELAY 0x1a
+#define ES9218_REG_GENERAL_CONFIG 0x1b
+#define ES9218_REG_RESERVED_2 0x1c
+#define ES9218_REG_GPIO_INV_CLOCK_GEAR 0x1d
+#define ES9218_REG_CHARGE_PUMP_CLK_LO 0x1e
+#define ES9218_REG_CHARGE_PUMP_CLK_HI 0x1f
+#define ES9218_REG_AMP_CONFIG 0x20
+#define ES9218_REG_INTERRUPT_MASK 0x21
+#define ES9218_REG_PROG_NCO_BIT0_7 0x22
+#define ES9218_REG_PROG_NCO_BIT8_15 0x23
+#define ES9218_REG_PROG_NCO_BIT16_23 0x24
+#define ES9218_REG_PROG_NCO_BIT24_31 0x25
+#define ES9218_REG_RESERVED_3 0x27
+#define ES9218_REG_FIR_RAM_ADDR 0x28
+#define ES9218_REG_FIR_DATA_BIT0_7 0x29
+#define ES9218_REG_FIR_DATA_BIT8_15 0x2a
+#define ES9218_REG_FIR_DATA_BIT16_23 0x2b
+#define ES9218_REG_PROG_FIR_CONFIG 0x2c
+#define ES9218_REG_ANALOG_OVERRIDE_1 0x2d
+#define ES9218_REG_ANALOG_OVERRIDE_2 0x2e
+#define ES9218_REG_ANALOG_OVERRIDE_3 0x2f
+#define ES9218_REG_ANALOG_CTRL 0x30
+#define ES9218_REG_CLKGEAR_CFG_BIT0_7 0x31
+#define ES9218_REG_CLKGEAR_CFG_BIT8_15 0x32
+#define ES9218_REG_CLKGEAR_CFG_BIT16_23 0x33
+#define ES9218_REG_RESERVED_4 0x34
+#define ES9218_REG_THD_COMP_C2_CH2_LO 0x35
+#define ES9218_REG_THD_COMP_C2_CH2_HI 0x36
+#define ES9218_REG_THD_COMP_C3_CH2_LO 0x37
+#define ES9218_REG_THD_COMP_C3_CH2_HI 0x38
+#define ES9218_REG_RESERVED_5 0x39
+#define ES9218_REG_RESERVED_6 0x3a
+#define ES9218_REG_RESERVED_7 0x3b
+#define ES9218_REG_RESERVED_8 0x3c
+#define ES9218_REG_CHIP_ID_AND_STATUS 0x40
+#define ES9218_REG_GPIO_AND_CLOCK_GEAR 0x41
+#define ES9218_REG_DPLL_NUMBER_BIT0_7 0x42
+#define ES9218_REG_DPLL_NUMBER_BIT8_15 0x43
+#define ES9218_REG_DPLL_NUMBER_BIT16_23 0x44
+#define ES9218_REG_DPLL_NUMBER_BIT24_31 0x45
+#define ES9218_REG_INPUT_MUTE_STATUS 0x48
+#define ES9218_REG_FIR_READ_BIT0_7 0x49
+#define ES9218_REG_FIR_READ_BIT8_15 0x4a
+#define ES9218_REG_FIR_READ_BIT16_23 0x4b
+
+enum es9218_clock_gear {
+ ES9218_CLK_GEAR_1 = 0, /* CLK = XI/1 */
+ ES9218_CLK_GEAR_2 = 1, /* CLK = XI/2 */
+ ES9218_CLK_GEAR_4 = 2, /* CLK = XI/4 */
+ ES9218_CLK_GEAR_8 = 3, /* CLK = XI/8 */
+};
+
+enum es9218_amp_mode {
+ ES9218_AMP_MODE_CORE_ON = 0,
+ ES9218_AMP_MODE_LOWFI = 1,
+ ES9218_AMP_MODE_1VRMS = 2,
+ ES9218_AMP_MODE_2VRMS = 3,
+};
+
+enum es9218_iface_role {
+ ES9218_IFACE_ROLE_SLAVE = 0,
+ ES9218_IFACE_ROLE_MASTER = 1,
+};
+
+enum es9218_iface_format {
+ ES9218_IFACE_FORMAT_I2S = 0,
+ ES9218_IFACE_FORMAT_LJUST = 1,
+ ES9218_IFACE_FORMAT_RJUST = 2,
+};
+
+enum es9218_iface_bits {
+ ES9218_IFACE_BITS_16 = 0,
+ ES9218_IFACE_BITS_24 = 1,
+ ES9218_IFACE_BITS_32 = 2,
+};
+
+enum es9218_filter_type {
+ ES9218_FILTER_LINEAR_FAST = 0,
+ ES9218_FILTER_LINEAR_SLOW = 1,
+ ES9218_FILTER_MINIMUM_FAST = 2,
+ ES9218_FILTER_MINIMUM_SLOW = 3,
+ ES9218_FILTER_APODIZING_1 = 4,
+ ES9218_FILTER_APODIZING_2 = 5,
+ ES9218_FILTER_HYBRID_FAST = 6,
+ ES9218_FILTER_BRICK_WALL = 7,
+};
+
+/* Power DAC on or off */
+extern void es9218_open(void);
+extern void es9218_close(void);
+
+/* Clock controls
+ *
+ * - Clock gear divides the input master clock to produce the DAC's clock.
+ * Frequency can be lowered to save power when using lower sample rates.
+ *
+ * - NCO (numerically controller oscillator), according to the datasheet,
+ * defines the ratio between the DAC's clock and the FSR (for PCM modes,
+ * this is I2S frame clock = sample rate). In master mode it effectively
+ * controls the sampling frequency by setting the I2S frame clock output.
+ * It can also be used in slave mode, but other parts of the datasheet
+ * say contradictory things about synchronous operation in slave mode.
+ *
+ * - If using NCO mode and a varying MCLK input (eg. input from the SoC) then
+ * you will need to call es9218_recompute_nco() when changing MCLK in order
+ * to refresh the NCO setting.
+ */
+extern void es9218_set_clock_gear(enum es9218_clock_gear gear);
+extern void es9218_set_nco_frequency(uint32_t fsr);
+extern void es9218_recompute_nco(void);
+
+/* Amplifier controls */
+extern void es9218_set_amp_mode(enum es9218_amp_mode mode);
+extern void es9218_set_amp_powered(bool en);
+
+/* Interface selection */
+extern void es9218_set_iface_role(enum es9218_iface_role role);
+extern void es9218_set_iface_format(enum es9218_iface_format fmt,
+ enum es9218_iface_bits bits);
+
+/* Volume controls, all volumes given in units of dB/10 */
+extern void es9218_set_dig_volume(int vol_l, int vol_r);
+extern void es9218_set_amp_volume(int vol);
+
+/* System mute */
+extern void es9218_mute(bool muted);
+
+/* Oversampling filter */
+extern void es9218_set_filter(enum es9218_filter_type filt);
+
+/* Automute settings */
+extern void es9218_set_automute_time(int time);
+extern void es9218_set_automute_level(int dB);
+extern void es9218_set_automute_fast_mode(bool en);
+
+/* DPLL bandwidth setting (knob = 0-15) */
+extern void es9218_set_dpll_bandwidth(int knob);
+
+/* THD compensation */
+extern void es9218_set_thd_compensation(bool en);
+extern void es9218_set_thd_coeffs(uint16_t c2, uint16_t c3);
+
+/* Direct register read/write/update operations */
+extern int es9218_read(int reg);
+extern void es9218_write(int reg, uint8_t val);
+extern void es9218_update(int reg, uint8_t msk, uint8_t val);
+
+/* GPIO pin setting callbacks */
+extern void es9218_set_power_pin(int level);
+extern void es9218_set_reset_pin(int level);
+
+/* XI(MCLK) getter -- supplied by the target.
+ *
+ * Note: when changing the supplied MCLK frequency, the NCO will need to be
+ * reprogrammed for the new master clock. Call es9218_recompute_nco() to
+ * force this. Not necessary if you're not using NCO mode.
+ */
+extern uint32_t es9218_get_mclk(void);
+
+#endif /* __ES9218_H__ */
diff --git a/firmware/export/events.h b/firmware/export/events.h
index 4591058d4f..8bdf1b55e2 100644
--- a/firmware/export/events.h
+++ b/firmware/export/events.h
@@ -52,7 +52,7 @@
#define EVENT_CLASS_RECORDING 0x1000
#define EVENT_CLASS_LCD 0x2000
#define EVENT_CLASS_VOICE 0x4000
-
+#define EVENT_CLASS_SYSTEM 0x8000 /*LAST ONE */
/**
* Subscribe to an event with a simple callback. The callback will be called
* synchronously everytime the event fires, passing the event id and data to
@@ -99,4 +99,14 @@ void remove_event_ex(unsigned short id, void (*handler)(unsigned short id, void
*/
void send_event(unsigned short id, void *data);
+/** System events **/
+enum {
+ /* USB_INSERTED
+ data = &usbmode */
+ SYS_EVENT_USB_INSERTED = (EVENT_CLASS_SYSTEM|1),
+ /* USB_EXTRACTED
+ data = NULL */
+ SYS_EVENT_USB_EXTRACTED,
+};
+
#endif
diff --git a/firmware/export/fat.h b/firmware/export/fat.h
index b8092290e6..70152985b5 100644
--- a/firmware/export/fat.h
+++ b/firmware/export/fat.h
@@ -140,6 +140,8 @@ enum fat_remove_op /* what should fat_remove(), remove? */
int fat_remove(struct fat_file *file, enum fat_remove_op what);
int fat_rename(struct fat_file *parent, struct fat_file *file,
const unsigned char *newname);
+int fat_modtime(struct fat_file *parent, struct fat_file *file,
+ time_t modtime);
/** File stream functions **/
int fat_closewrite(struct fat_filestr *filestr, uint32_t size,
@@ -174,7 +176,6 @@ void fat_recalc_free(IF_MV_NONVOID(int volume));
bool fat_size(IF_MV(int volume,) unsigned long *size, unsigned long *free);
/** Misc. **/
-time_t fattime_mktime(uint16_t fatdate, uint16_t fattime);
void fat_empty_fat_direntry(struct fat_direntry *entry);
void fat_init(void);
diff --git a/firmware/export/font.h b/firmware/export/font.h
index 067c67e43d..2334a8bd1a 100644
--- a/firmware/export/font.h
+++ b/firmware/export/font.h
@@ -117,7 +117,7 @@ struct font {
/* font routines*/
void font_init(void) INIT_ATTR;
-const char* font_filename(int font_id);
+bool font_filename_matches_loaded_id(int font_id, const char *filename);
int font_load(const char *path);
int font_load_ex(const char *path, size_t buffer_size, int glyphs);
void font_unload(int font_id);
@@ -132,7 +132,7 @@ void font_disable_all(void);
void font_enable_all(void);
struct font* font_get(int font);
-
+int font_getstringnsize(const unsigned char *str, size_t maxbytes, int *w, int *h, int fontnumber);
int font_getstringsize(const unsigned char *str, int *w, int *h, int fontnumber);
int font_get_width(struct font* ft, unsigned short ch);
const unsigned char * font_get_bits(struct font* ft, unsigned short ch);
diff --git a/firmware/target/arm/pnx0101/iriver-ifp7xx/usb-ifp7xx.c b/firmware/export/ft6x06.h
index 0bfcdf3b1a..6596f89272 100644
--- a/firmware/target/arm/pnx0101/iriver-ifp7xx/usb-ifp7xx.c
+++ b/firmware/export/ft6x06.h
@@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Copyright (C) 2006 by Barry Wardell
+ * Copyright (C) 2021 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -18,32 +18,42 @@
* KIND, either express or implied.
*
****************************************************************************/
+
+#ifndef __FT6x06_H__
+#define __FT6x06_H__
+
#include "config.h"
-#include "cpu.h"
-#include "kernel.h"
-#include "thread.h"
-#include "system.h"
-#include "debug.h"
-#include "ata.h"
-#include "disk.h"
-#include "panic.h"
-#include "lcd.h"
-#include "usb.h"
-#include "button.h"
-#include "string.h"
-
-void usb_init_device(void)
-{
-}
-
-int usb_detect(void)
-{
- /* TODO: Implement USB_ISP1582 */
- return USB_EXTRACTED;
-}
-
-void usb_enable(bool on)
-{
- /* TODO: Implement USB_ISP1582 */
- (void)on;
-}
+#include <stdbool.h>
+
+enum ft6x06_event {
+ FT6x06_EVT_NONE = -1,
+ FT6x06_EVT_PRESS = 0,
+ FT6x06_EVT_RELEASE = 1,
+ FT6x06_EVT_CONTACT = 2,
+};
+
+struct ft6x06_point {
+ int event;
+ int touch_id;
+ int pos_x;
+ int pos_y;
+ int weight;
+ int area;
+};
+
+struct ft6x06_state {
+ int gesture;
+ int nr_points;
+ struct ft6x06_point points[FT6x06_NUM_POINTS];
+};
+
+extern struct ft6x06_state ft6x06_state;
+
+typedef void(*ft6x06_event_cb)(struct ft6x06_state* state);
+
+void ft6x06_init(void);
+void ft6x06_set_event_cb(ft6x06_event_cb fn);
+void ft6x06_enable(bool en);
+void ft6x06_irq_handler(void);
+
+#endif /* __FT6x06_H__ */
diff --git a/firmware/export/gdb_api.h b/firmware/export/gdb_api.h
index 1836c5992e..d2358ee3bb 100644
--- a/firmware/export/gdb_api.h
+++ b/firmware/export/gdb_api.h
@@ -25,9 +25,6 @@
#include "config.h"
#define GDB_API_MAGIC 0x6db570b
-#ifdef IRIVER_IFP7XX_SERIES
-#define GDB_API_ADDRESS 0xc06000
-#endif
struct gdb_api
{
diff --git a/firmware/export/generic_i2c.h b/firmware/export/generic_i2c.h
index f71736acf0..9fdba26836 100644
--- a/firmware/export/generic_i2c.h
+++ b/firmware/export/generic_i2c.h
@@ -49,5 +49,15 @@ int i2c_write_data(int bus_index, int bus_address, int address,
int i2c_read_data(int bus_index, int bus_address, int address,
unsigned char* buf, int count);
+/* Special function for devices that can appear on I2C bus but do not
+ * comply to I2C specification. Such devices include AT88SC6416C crypto
+ * memory. To read data from AT88SC6416C, a write I2C transaction starts,
+ * 3 bytes are written and then, in the middle of transaction, the device
+ * starts sending data.
+ */
+int i2c_write_read_data(int bus_index, int bus_address,
+ const unsigned char* buf_write, int count_write,
+ unsigned char* buf_read, int count_read);
+
#endif /* _GEN_I2C_ */
diff --git a/firmware/export/i2c-async.h b/firmware/export/i2c-async.h
index 2877d1875c..f31a73452b 100644
--- a/firmware/export/i2c-async.h
+++ b/firmware/export/i2c-async.h
@@ -247,14 +247,15 @@ extern int i2c_reg_modify1(int bus, uint8_t addr, uint8_t reg,
uint8_t clr, uint8_t set, uint8_t* val);
/* Variant to write a single 8-bit value to a register */
-inline int i2c_reg_write1(int bus, uint8_t addr, uint8_t reg, uint8_t val)
+static inline int i2c_reg_write1(int bus, uint8_t addr,
+ uint8_t reg, uint8_t val)
{
return i2c_reg_write(bus, addr, reg, 1, &val);
}
/* Variant to read an 8-bit value from a register; returns the value
* directly, or returns -1 on any error. */
-inline int i2c_reg_read1(int bus, uint8_t addr, uint8_t reg)
+static inline int i2c_reg_read1(int bus, uint8_t addr, uint8_t reg)
{
uint8_t v;
int i = i2c_reg_read(bus, addr, reg, 1, &v);
@@ -265,8 +266,8 @@ inline int i2c_reg_read1(int bus, uint8_t addr, uint8_t reg)
}
/* Variant to set or clear one bit in an 8-bit register */
-inline int i2c_reg_setbit1(int bus, uint8_t addr, uint8_t reg,
- int bit, int value, uint8_t* val)
+static inline int i2c_reg_setbit1(int bus, uint8_t addr, uint8_t reg,
+ int bit, int value, uint8_t* val)
{
uint8_t clr = 0, set = 0;
if(value)
diff --git a/firmware/export/i2c-coldfire.h b/firmware/export/i2c-coldfire.h
index b0d21a8631..f98daf5d5f 100644
--- a/firmware/export/i2c-coldfire.h
+++ b/firmware/export/i2c-coldfire.h
@@ -30,7 +30,7 @@
#include "cpu.h"
-void i2c_init(void);
+void i2c_init(void) INIT_ATTR;
int i2c_read (volatile unsigned char *iface, unsigned char addr,
unsigned char *buf, int count);
int i2c_write(volatile unsigned char *iface, unsigned char addr,
diff --git a/firmware/export/i2c-rk27xx.h b/firmware/export/i2c-rk27xx.h
index 96baf566a7..d16d4c3dbb 100644
--- a/firmware/export/i2c-rk27xx.h
+++ b/firmware/export/i2c-rk27xx.h
@@ -24,7 +24,7 @@
#include "config.h"
-void i2c_init(void);
+void i2c_init(void) INIT_ATTR;
int i2c_write(unsigned char slave, int address, int len, const unsigned char *data);
int i2c_read(unsigned char slave, int address, int len, unsigned char *data);
diff --git a/firmware/export/i2c-s5l8700.h b/firmware/export/i2c-s5l8700.h
index e6ffcfae55..607f361384 100644
--- a/firmware/export/i2c-s5l8700.h
+++ b/firmware/export/i2c-s5l8700.h
@@ -24,7 +24,7 @@
#include "config.h"
-void i2c_init(void);
+void i2c_init(void) INIT_ATTR;
int i2c_write(unsigned char slave, int address, int len, const unsigned char *data);
int i2c_read(unsigned char slave, int address, int len, unsigned char *data);
diff --git a/firmware/export/i2c-s5l8702.h b/firmware/export/i2c-s5l8702.h
index f671e4059e..a1e7be04b4 100644
--- a/firmware/export/i2c-s5l8702.h
+++ b/firmware/export/i2c-s5l8702.h
@@ -24,7 +24,7 @@
#include "config.h"
-void i2c_init(void);
+void i2c_init(void) INIT_ATTR;
int i2c_write(int bus, unsigned char slave, int address, int len, const unsigned char *data);
int i2c_read(int bus, unsigned char slave, int address, int len, unsigned char *data);
diff --git a/firmware/export/i2c.h b/firmware/export/i2c.h
index ac9ddba055..08562b198c 100644
--- a/firmware/export/i2c.h
+++ b/firmware/export/i2c.h
@@ -21,7 +21,9 @@
#ifndef I2C_H
#define I2C_H
-extern void i2c_init(void);
+#include "config.h"
+
+extern void i2c_init(void) INIT_ATTR;
extern void i2c_begin(void);
extern void i2c_end(void);
extern int i2c_write(int device, const unsigned char* buf, int count );
diff --git a/firmware/export/ifp_usb_serial.h b/firmware/export/ifp_usb_serial.h
deleted file mode 100644
index b56e5fff86..0000000000
--- a/firmware/export/ifp_usb_serial.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2006 by Tomasz Malesinski
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ****************************************************************************/
-#ifndef _USB_SERIAL_H_
-#define _USB_SERIAL_H_
-
-void usb_serial_init(void);
-void usb_serial_put_byte(int b);
-int usb_serial_get_byte(void);
-int usb_serial_try_put_byte(int b);
-int usb_serial_try_get_byte(void);
-
-#endif
diff --git a/firmware/export/jz4740.h b/firmware/export/jz4740.h
index 379c9f8aaa..354379a950 100644
--- a/firmware/export/jz4740.h
+++ b/firmware/export/jz4740.h
@@ -4986,6 +4986,7 @@ do{ \
#define IPU_V_BASE 0xB3080000
#define IPU__SIZE 0x00001000
+#ifndef __ASSEMBLY__
struct ipu_module
{
unsigned int reg_ctrl; // 0x0
@@ -5022,7 +5023,7 @@ struct Ration2m
float ratio;
int n, m;
};
-
+#endif /* !__ASSEMBLY__ */
// Register offset
#define REG_CTRL 0x0
diff --git a/firmware/export/jz4760b.h b/firmware/export/jz4760b.h
index 589f67800a..af1f36270b 100644
--- a/firmware/export/jz4760b.h
+++ b/firmware/export/jz4760b.h
@@ -7017,6 +7017,11 @@ do { \
#define USB_INTR_SUSPEND 0x01
#define USB_INTR_RESUME 0x02
#define USB_INTR_RESET 0x04
+#define USB_INTR_SOF 0x08
+#define USB_INTR_CONNECT 0x10
+#define USB_INTR_DISCONNECT 0x20
+#define USB_INTR_SESS_REQ 0x40
+#define USB_INTR_VBUS_ERR 0x80
#define USB_INTR_EP(n) (1 << (n))
diff --git a/firmware/export/lcd.h b/firmware/export/lcd.h
index ffaf1a63d2..67b22190ad 100644
--- a/firmware/export/lcd.h
+++ b/firmware/export/lcd.h
@@ -82,7 +82,7 @@ enum screen_type {
struct scrollinfo;
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
#define STRIDE_MAIN(w, h) (h)
#else
#define STRIDE_MAIN(w, h) (w)
@@ -178,9 +178,12 @@ struct frame_buffer_t {
#define VP_IS_RTL(vp) (((vp)->flags & VP_FLAG_ALIGNMENT_MASK) == VP_FLAG_ALIGN_RIGHT)
-#define VP_FLAG_VP_DIRTY 0x4000
-#define VP_FLAG_CLEAR_FLAG 0x8000
+#define VP_FLAG_OWNER_UPDATE 0x2000 /* block update_vp functions */
+#define VP_FLAG_VP_DIRTY 0x4000
+#define VP_FLAG_CLEAR_FLAG 0x8000
#define VP_FLAG_VP_SET_CLEAN (VP_FLAG_CLEAR_FLAG | VP_FLAG_VP_DIRTY)
+/* flags set by viewport_set_defaults() */
+#define VP_DEFAULT_FLAGS (VP_FLAG_VP_DIRTY)
struct viewport {
int x;
@@ -493,7 +496,7 @@ typedef void lcd_blockfunc_type(fb_data *address, unsigned mask, unsigned bits);
#endif
#ifndef LCD_NBELEMS
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
#define LCD_NBELEMS(w, h) (((w-1)*STRIDE_MAIN(w, h)) + h)
#else
#define LCD_NBELEMS(w, h) (((h-1)*STRIDE_MAIN(w, h)) + w)
@@ -507,7 +510,9 @@ typedef void lcd_blockfunc_type(fb_data *address, unsigned mask, unsigned bits);
extern struct viewport* lcd_current_viewport;
-#define FBADDR(x,y) ((fb_data*) lcd_current_viewport->buffer->get_address_fn(x, y))
+#define FB_CURRENTVP_BUFFER (lcd_current_viewport->buffer)
+#define FBADDRBUF(buffer,x,y) ((fb_data*) buffer->get_address_fn(x,y))
+#define FBADDR(x,y) (FBADDRBUF(lcd_current_viewport->buffer,x,y))
#define FRAMEBUFFER_SIZE (sizeof(fb_data)*LCD_FBWIDTH*LCD_FBHEIGHT)
diff --git a/firmware/export/linuxboot.h b/firmware/export/linuxboot.h
new file mode 100644
index 0000000000..de6f24bf57
--- /dev/null
+++ b/firmware/export/linuxboot.h
@@ -0,0 +1,192 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2022 Aidan MacDonald
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef __LINUXBOOT_H__
+#define __LINUXBOOT_H__
+
+#include "system.h"
+#include <stddef.h>
+#include <sys/types.h>
+
+/*
+ * From u-boot's include/image.h
+ * SPDX-License-Identifier: GPL-2.0+
+ * (C) Copyright 2008 Semihalf
+ * (C) Copyright 2000-2005
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ */
+
+#define IH_MAGIC 0x27051956
+#define IH_NMLEN 32
+
+enum
+{
+ IH_ARCH_INVALID,
+ IH_ARCH_ALPHA,
+ IH_ARCH_ARM,
+ IH_ARCH_I386,
+ IH_ARCH_IA64,
+ IH_ARCH_MIPS,
+ IH_ARCH_MIPS64,
+ IH_ARCH_PPC,
+ IH_ARCH_S390,
+ IH_ARCH_SH,
+ IH_ARCH_SPARC,
+ IH_ARCH_SPARC64,
+ IH_ARCH_M68K,
+ /* NOTE: Other archs not relevant and omitted here, can add if needed */
+ IH_ARCH_COUNT,
+};
+
+enum
+{
+ IH_TYPE_INVALID = 0,
+ IN_TYPE_STANDALONE,
+ IH_TYPE_KERNEL,
+ IH_TYPE_RAMDISK,
+ IH_TYPE_MULTI,
+ IH_TYPE_FIRMWARE,
+ IH_TYPE_SCRIPT,
+ IH_TYPE_FILESYSTEM,
+ IH_TYPE_FLATDT,
+ /* NOTE: Other types not relevant and omitted here, can add if needed */
+ IH_TYPE_COUNT,
+};
+
+enum
+{
+ IH_COMP_NONE = 0,
+ IH_COMP_GZIP,
+ IH_COMP_BZIP2,
+ IH_COMP_LZMA,
+ IH_COMP_LZO,
+ IH_COMP_LZ4,
+ IH_COMP_ZSTD,
+ IH_COMP_COUNT,
+};
+
+/* Legacy U-Boot image header as produced by mkimage(1).
+ *
+ * WARNING: all fields are big-endian so you usually do not want to
+ * access them directly, use the accessor functions instead.
+ */
+struct uimage_header
+{
+ uint32_t ih_magic;
+ uint32_t ih_hcrc;
+ uint32_t ih_time;
+ uint32_t ih_size;
+ uint32_t ih_load;
+ uint32_t ih_ep;
+ uint32_t ih_dcrc;
+ uint8_t ih_os;
+ uint8_t ih_arch;
+ uint8_t ih_type;
+ uint8_t ih_comp;
+ uint8_t ih_name[IH_NMLEN];
+};
+
+#define _uimage_get32_f(name) \
+ static inline uint32_t uimage_get_##name(const struct uimage_header* uh) \
+ { return betoh32(uh->ih_##name); }
+_uimage_get32_f(magic)
+_uimage_get32_f(hcrc)
+_uimage_get32_f(time)
+_uimage_get32_f(size)
+_uimage_get32_f(load)
+_uimage_get32_f(ep)
+_uimage_get32_f(dcrc)
+#undef _uimage_get32_f
+
+#define _uimage_set32_f(name) \
+ static inline void uimage_set_##name(struct uimage_header* uh, uint32_t val) \
+ { uh->ih_##name = htobe32(val); }
+_uimage_set32_f(magic)
+_uimage_set32_f(hcrc)
+_uimage_set32_f(time)
+_uimage_set32_f(size)
+_uimage_set32_f(load)
+_uimage_set32_f(ep)
+_uimage_set32_f(dcrc)
+#undef _uimage_set32_f
+
+#define _uimage_get8_f(name) \
+ static inline uint8_t uimage_get_##name(const struct uimage_header* uh) \
+ { return uh->ih_##name; }
+_uimage_get8_f(os)
+_uimage_get8_f(arch)
+_uimage_get8_f(type)
+_uimage_get8_f(comp)
+#undef _uimage_get8_f
+
+#define _uimage_set8_f(name) \
+ static inline void uimage_set_##name(struct uimage_header* uh, uint8_t val) \
+ { uh->ih_##name = val; }
+_uimage_set8_f(os)
+_uimage_set8_f(arch)
+_uimage_set8_f(type)
+_uimage_set8_f(comp)
+#undef _uimage_set8_f
+
+/*
+ * uImage utilities
+ */
+
+/** Reader callback for use with `uimage_load`
+ *
+ * \param buf Buffer to write into
+ * \param size Number of bytes to read
+ * \param ctx State argument
+ * \return Number of bytes actually read, or -1 on error.
+ */
+typedef ssize_t(*uimage_reader)(void* buf, size_t size, void* ctx);
+
+/** Calculate U-Boot style CRC */
+uint32_t uimage_crc(uint32_t crc, const void* data, size_t size);
+
+/** Calculate CRC of a uImage header */
+uint32_t uimage_calc_hcrc(const struct uimage_header* uh);
+
+/** Load and decompress a uImage
+ *
+ * \param uh Returned header struct (will be filled with read data)
+ * \param out_size Returned size of the decompressed data
+ * \param reader Data reader function
+ * \param rctx Context argument for the reader function
+ * \return Buflib handle containing the decompressed data, or negative on error
+ *
+ * This function will read a uImage, verify checksums and decompress the image
+ * data into a non-moveable buflib allocation. The length of the compressed
+ * data will be taken from the uImage header.
+ */
+int uimage_load(struct uimage_header* uh, size_t* out_size,
+ uimage_reader reader, void* rctx);
+
+/** File reader for use with `uimage_load`
+ *
+ * \param ctx File descriptor, casted to `void*`
+ */
+ssize_t uimage_fd_reader(void* buf, size_t size, void* ctx);
+
+/* helper for patching broken self-extracting kernels on MIPS */
+uint32_t mips_linux_stub_get_entry(void** code_start, size_t code_size);
+
+#endif /* __LINUXBOOT_H__ */
diff --git a/firmware/export/logf.h b/firmware/export/logf.h
index c8aaad06b4..7fbe5976a4 100644
--- a/firmware/export/logf.h
+++ b/firmware/export/logf.h
@@ -31,7 +31,7 @@
#define MAX_LOGF_SIZE 16384
-extern unsigned char logfbuffer[MAX_LOGF_SIZE];
+extern unsigned char logfbuffer[MAX_LOGF_SIZE + 1];
extern int logfindex;
extern bool logfwrap;
extern bool logfenabled;
diff --git a/firmware/export/mi4-loader.h b/firmware/export/mi4-loader.h
index f66164ec2c..adc43ebf64 100644
--- a/firmware/export/mi4-loader.h
+++ b/firmware/export/mi4-loader.h
@@ -21,6 +21,9 @@
*
****************************************************************************/
+#ifndef __MI4_LOADER_H__
+#define __MI4_LOADER_H__
+
#include <stdint.h>
#define MI4_HEADER_SIZE 0x200
@@ -50,21 +53,4 @@ struct tea_key {
int load_mi4(unsigned char* buf, const char* firmware, unsigned int buffer_size);
const char *mi4_strerror(int8_t errno);
-#ifdef HAVE_MULTIBOOT /* defined by config.h */
-/* Check in root of this <volume> for rockbox_main.<playername>
- * if this file empty or there is a single slash '/'
- * buf = '<volume#>/<rootdir>/<firmware(name)>\0'
- * If instead '/<*DIRECTORY*>' is supplied
- * addpath will be set to this DIRECTORY buf =
- * '/<volume#>/addpath/<rootdir>/<firmware(name)>\0'
- * On error returns Negative number or 0
- * On success returns bytes from snprintf
- * and generated path will be placed in buf
- * note: if supplied buffer is too small return will be
- * the number of bytes that would have been written
- */
-
-/* TODO needs mapped back to debug_menu if root redirect ever becomes a reality */
-int get_redirect_dir(char* buf, int buffer_size, int volume,
- const char* rootdir, const char* firmware);
-#endif
+#endif /* __MI4_LOADER_H__ */
diff --git a/firmware/target/arm/tatung/tpj1022/adc-target.h b/firmware/export/multiboot.h
index 026bb58089..0132b8531f 100644
--- a/firmware/target/arm/tatung/tpj1022/adc-target.h
+++ b/firmware/export/multiboot.h
@@ -5,9 +5,8 @@
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
- * $Id$
*
- * Copyright (C) 2006 by Barry Wardell
+ * Copyright (C) 2017, 2020 by William Wilgus
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -18,18 +17,14 @@
* KIND, either express or implied.
*
****************************************************************************/
-#ifndef _ADC_TARGET_H_
-#define _ADC_TARGET_H_
-#define NUM_ADC_CHANNELS 4
-
-#define ADC_BATTERY 0
-#define ADC_UNKNOWN_1 1
-#define ADC_UNKNOWN_2 2
-#define ADC_SCROLLPAD 3
-#define ADC_UNREG_POWER ADC_BATTERY /* For compatibility */
-
-/* Force a scan now */
-unsigned short adc_scan(int channel);
+#ifndef __MULTIBOOT_H__
+#define __MULTIBOOT_H__
+extern int write_bootdata(unsigned char* buf, int len, unsigned int boot_volume);
+#ifdef HAVE_MULTIBOOT
+extern int get_redirect_dir(char* buf, int buffer_size, int volume,
+ const char* rootdir, const char* firmware);
#endif
+
+#endif /* __MULTIBOOT_H__ */
diff --git a/firmware/export/mv.h b/firmware/export/mv.h
index 3657ef6c98..966ff9a032 100644
--- a/firmware/export/mv.h
+++ b/firmware/export/mv.h
@@ -87,6 +87,10 @@
#define VOL_MAX_LEN (1 + VOL_DEC_MAX_LEN + 2 + 1)
#define VOL_NUM_MAX 100
+#ifndef ROOT_VOLUME
+#define ROOT_VOLUME INT_MAX
+#endif
+
#else /* empty definitions if no multi-volume */
#define IF_MV(x...)
#define IF_MV_NONVOID(x...) void
@@ -99,6 +103,13 @@
#define CHECK_DRV(drive) \
((unsigned int)IF_MD_DRV(drive) < NUM_DRIVES)
+/* contains info about a volume */
+struct volumeinfo
+{
+ int drive; /* drive number */
+ int partition; /* partition number (0 for superfloppy drives) */
+};
+
/* Volume-centric functions (in disk.c) */
void volume_recalc_free(IF_MV_NONVOID(int volume));
unsigned int volume_get_cluster_size(IF_MV_NONVOID(int volume));
@@ -121,4 +132,6 @@ static inline int volume_drive(int volume)
}
#endif /* HAVE_MULTIDRIVE */
+int volume_partition(int volume);
+
#endif /* __MV_H__ */
diff --git a/firmware/export/pathfuncs.h b/firmware/export/pathfuncs.h
index 92539c54c1..1b18f22d06 100644
--- a/firmware/export/pathfuncs.h
+++ b/firmware/export/pathfuncs.h
@@ -30,10 +30,15 @@
/* useful char constants that could be reconfigured if desired */
#define PATH_SEPCH '/'
#define PATH_SEPSTR "/"
+#define PATH_ROOTCHR '/'
#define PATH_ROOTSTR "/"
#define PATH_BADSEPCH '\\'
#define PATH_DRVSEPCH ':'
+#ifndef ROOT_VOLUME
+#define ROOT_VOLUME INT_MAX
+#endif
+
/* a nicer way to check for "." and ".." than two strcmp() calls */
static inline bool is_dotdir_name(const char *name)
{
@@ -74,7 +79,9 @@ static inline bool name_is_dot_dot(const char *name)
#ifdef HAVE_MULTIVOLUME
int path_strip_volume(const char *name, const char **nameptr, bool greedy);
+int path_strip_last_volume(const char *name, const char **nameptr, bool greedy);
int get_volume_name(int volume, char *name);
+int make_volume_root(int volume, char *dst);
#endif
int path_strip_drive(const char *name, const char **nameptr, bool greedy);
@@ -82,10 +89,13 @@ size_t path_basename(const char *name, const char **nameptr);
size_t path_dirname(const char *name, const char **nameptr);
size_t path_strip_trailing_separators(const char *name, const char **nameptr);
void path_correct_separators(char *dstpath, const char *path);
+void path_remove_dot_segments(char *dstpath, const char *path);
/* constants useable in basepath and component */
#define PA_SEP_HARD NULL /* separate even if base is empty */
#define PA_SEP_SOFT "" /* separate only if base is nonempty */
+size_t path_append_ex(char *buf, const char *basepath, size_t basepath_max,
+ const char *component, size_t bufsize);
size_t path_append(char *buffer, const char *basepath, const char *component,
size_t bufsize);
ssize_t parse_path_component(const char **pathp, const char **namep);
diff --git a/firmware/export/pcm-internal.h b/firmware/export/pcm-internal.h
index d5d46c33e1..e6400004a0 100644
--- a/firmware/export/pcm-internal.h
+++ b/firmware/export/pcm-internal.h
@@ -121,7 +121,6 @@ pcm_play_dma_status_callback(enum pcm_dma_status status)
void pcm_play_dma_start_int(const void *addr, size_t size);
void pcm_play_dma_stop_int(void);
void pcm_play_stop_int(void);
-const void *pcm_play_dma_get_peak_buffer_int(int *count);
#endif /* HAVE_SW_VOLUME_CONTROL && !PCM_SW_VOLUME_UNBUFFERED */
/* Called by the bottom layer ISR when more data is needed. Returns true
@@ -145,7 +144,6 @@ void pcm_play_dma_init(void) INIT_ATTR;
void pcm_play_dma_postinit(void);
void pcm_play_dma_start(const void *addr, size_t size);
void pcm_play_dma_stop(void);
-const void * pcm_play_dma_get_peak_buffer(int *count);
void pcm_dma_apply_settings(void);
diff --git a/firmware/export/pcm_sampr.h b/firmware/export/pcm_sampr.h
index 70f2dc2ba2..fc48a943fd 100644
--- a/firmware/export/pcm_sampr.h
+++ b/firmware/export/pcm_sampr.h
@@ -427,10 +427,10 @@ extern const unsigned long rec_freq_sampr[REC_NUM_FREQ];
#ifdef CONFIG_SAMPR_TYPES
-#define SAMPR_TYPE_MASK (0xff << 24)
-#define SAMPR_TYPE_PLAY (0x00 << 24)
+#define SAMPR_TYPE_MASK (0xffu << 24)
+#define SAMPR_TYPE_PLAY (0x00u << 24)
#ifdef HAVE_RECORDING
-#define SAMPR_TYPE_REC (0x01 << 24)
+#define SAMPR_TYPE_REC (0x01u << 24)
#endif
#ifndef PCM_SAMPR_CONFIG_ONLY
diff --git a/firmware/export/powermgmt.h b/firmware/export/powermgmt.h
index 235f5302f9..a78386445e 100644
--- a/firmware/export/powermgmt.h
+++ b/firmware/export/powermgmt.h
@@ -70,6 +70,12 @@ extern unsigned int power_thread_inputs;
#endif /* CONFIG_CHARGING */
+enum shutdown_type
+{
+ SHUTDOWN_POWER_OFF,
+ SHUTDOWN_REBOOT,
+};
+
#if CONFIG_CHARGING == CHARGING_TARGET
/* Include target-specific definitions */
#include "powermgmt-target.h"
@@ -78,43 +84,20 @@ extern unsigned int power_thread_inputs;
/* Start up power management thread */
void powermgmt_init(void) INIT_ATTR;
-/* Generic current values that are intentionally meaningless - config header
- * should define proper numbers.*/
-
-
-#ifndef CURRENT_BACKLIGHT
-#define CURRENT_BACKLIGHT 5 /* additional current when backlight always on */
-#endif
-
-#if defined(HAVE_RECORDING) && !defined(CURRENT_RECORD)
-#define CURRENT_RECORD 2 /* additional recording current */
-#endif /* HAVE_RECORDING && !CURRENT_RECORD*/
-
-#ifndef CURRENT_USB
-#define CURRENT_USB 2 /* usual current in mA in USB mode */
-#endif
-
-#if defined(HAVE_REMOTE_LCD) && !defined(CURRENT_REMOTE)
-#define CURRENT_REMOTE 2 /* additional current when remote connected */
-#endif /* CURRENT_REMOTE && !HAVE_REMOTE_LCD */
-
-#if CONFIG_CHARGING
-#ifndef CURRENT_MAX_CHG
+#if CONFIG_CHARGING && !defined(CURRENT_MAX_CHG)
#define CURRENT_MAX_CHG 350 /* maximum charging current */
#endif
-#endif /* CONFIG_CHARGING */
-
-#ifdef CHARGING_DEBUG_FILE
-#define POWERMGMT_DEBUG_STACK ((0x1000)/sizeof(long))
-#else
-#define POWERMGMT_DEBUG_STACK 0
-#endif
#ifndef BATT_AVE_SAMPLES
/* slw filter constant unless otherwise specified */
#define BATT_AVE_SAMPLES 128
#endif
+#ifndef BATT_CURRENT_AVE_SAMPLES
+/* semi arbitrary but needs to be 'large' for the time estimation algorithm */
+#define BATT_CURRENT_AVE_SAMPLES 128
+#endif
+
#ifndef POWER_THREAD_STEP_TICKS
/* 2HZ sample rate unless otherwise specified */
#define POWER_THREAD_STEP_TICKS (HZ/2)
@@ -132,11 +115,15 @@ extern const unsigned short percent_to_volt_charge[11];
int battery_level(void); /* percent */
int battery_time(void); /* minutes */
int battery_voltage(void); /* filtered batt. voltage in millivolts */
+int battery_current(void); /* battery current in milliamps
+ * (may just be a rough estimate) */
/* Implemented by the target, unfiltered */
int _battery_level(void); /* percent */
int _battery_time(void); /* minutes */
int _battery_voltage(void); /* voltage in millivolts */
+int _battery_current(void); /* (dis)charge current in milliamps */
+
#if CONFIG_CHARGING >= CHARGING_TARGET
void powermgmt_init_target(void);
void charging_algorithm_close(void);
@@ -163,19 +150,23 @@ void battery_read_info(int *voltage, int *level);
bool battery_level_safe(void);
void set_poweroff_timeout(int timeout);
+#if BATTERY_CAPACITY_INC > 0
void set_battery_capacity(int capacity); /* set local battery capacity value */
-int get_battery_capacity(void); /* get local battery capacity value */
+#endif
+int get_battery_capacity(void); /* get local battery capacity value */
void set_battery_type(int type); /* set local battery type */
void set_sleeptimer_duration(int minutes);
+bool get_sleep_timer_active(void);
int get_sleep_timer(void);
void set_keypress_restarts_sleep_timer(bool enable);
void handle_auto_poweroff(void);
void set_car_adapter_mode(bool setting);
void reset_poweroff_timer(void);
void cancel_shutdown(void);
-void shutdown_hw(void);
+void shutdown_hw(enum shutdown_type sd_type);
void sys_poweroff(void);
+void sys_reboot(void);
/* Returns true if the system should force shutdown for some reason -
* eg. low battery */
bool query_force_shutdown(void);
diff --git a/firmware/export/rbpaths.h b/firmware/export/rbpaths.h
index a3042d80bc..9dd0a24c6f 100644
--- a/firmware/export/rbpaths.h
+++ b/firmware/export/rbpaths.h
@@ -52,6 +52,9 @@
#define PLUGIN_DIR ROCKBOX_DIR "/rocks"
#define CODECS_DIR ROCKBOX_DIR "/codecs"
+#define RB_ROOT_VOL_HIDDEN(v) (IF_MV_VOL(v) == 0)
+#define RB_ROOT_CONTENTS_DIR "/" IF_MV("<0>")
+
#else /* APPLICATION */
#define HOME_DIR "<HOME>" /* replaced at runtime */
diff --git a/firmware/export/rectangle.h b/firmware/export/rectangle.h
new file mode 100644
index 0000000000..0962611791
--- /dev/null
+++ b/firmware/export/rectangle.h
@@ -0,0 +1,75 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2022 Aidan MacDonald
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef _RECTANGLE_H_
+#define _RECTANGLE_H_
+
+#include <stdbool.h>
+
+struct rectangle
+{
+ int x, y, w, h;
+};
+
+/** Returns true if the rectangle is non-degenerate (positive width/height). */
+static inline bool rect_valid(const struct rectangle *r)
+{
+ return r->w > 0 && r->h > 0;
+}
+
+/** Returns true if ra contains rb. */
+bool rect_contains(const struct rectangle *ra, const struct rectangle *rb);
+
+/** Returns true if ra and rb overlap. */
+bool rect_overlap(const struct rectangle *ra, const struct rectangle *rb);
+
+/**
+ * Computes the biggest rectangle contained in ra and rb.
+ * Returns true if the intersection exists, and writes it to r_out.
+ *
+ * Returns false if there is no intersection, or either input
+ * rectangle is degenerate. In this case r_out may be uninitialized.
+ */
+bool rect_intersect(const struct rectangle *ra, const struct rectangle *rb,
+ struct rectangle *r_out);
+
+/**
+ * Computes the smallest rectangle containing both ra and rb.
+ *
+ * If one input is degenerate, and the other is not, the output is a
+ * copy of the non-degenerate input. If both inputs are degenerate,
+ * then the output is degenerate but is otherwise unspecified.
+ */
+void rect_union(const struct rectangle *ra, const struct rectangle *rb,
+ struct rectangle *r_out);
+
+/**
+ * Computes the result of subtracting 'rsub' from 'rect'. Up to four
+ * non-overlapping output rectangles will written to 'rects_out'; the
+ * return value is the number of rectangles written.
+ *
+ * If 'rsub' does not overlap 'rect', or if either of the inputs are
+ * degenerate, the output is a single rectangle: a copy of 'rect'.
+ */
+int rect_difference(const struct rectangle *rect,
+ const struct rectangle *rsub,
+ struct rectangle rects_out[4]);
+
+#endif /* _RECTANGLE_H_ */
diff --git a/firmware/export/screendump.h b/firmware/export/screendump.h
index cd7793b9d3..b8fc27047d 100644
--- a/firmware/export/screendump.h
+++ b/firmware/export/screendump.h
@@ -52,12 +52,7 @@
#define DUMP_BMP_LINESIZE ((LCD_WIDTH*3 + 3) & ~3)
#endif
-#ifdef BOOTLOADER
-
-#define screen_dump()
-#define remote_screen_dump()
-
-#else /* !BOOTLOADER */
+#ifdef HAVE_SCREENDUMP
/* Save a .BMP file containing the current screen contents. */
void screen_dump(void);
@@ -69,6 +64,11 @@ void screen_dump_set_hook(void (*hook)(int fd));
void remote_screen_dump(void);
#endif
-#endif /* !BOOTLOADER */
+#else /* !HAVE_SCREENDUMP */
+
+#define screen_dump() do { } while(0)
+#define remote_screen_dump() do { } while(0)
+
+#endif /* HAVE_SCREENDUMP */
#endif /* __SCREENDUMP_H__ */
diff --git a/firmware/export/si4700.h b/firmware/export/si4700.h
index bd75bf0817..cf9e0f077b 100644
--- a/firmware/export/si4700.h
+++ b/firmware/export/si4700.h
@@ -49,13 +49,11 @@ void si4700_rds_init(void) INIT_ATTR;
/* Radio is fully powered up or about to be powered down */
void si4700_rds_powerup(bool on);
-#if (CONFIG_RDS & RDS_CFG_ISR)
-/* Read raw RDS info for processing - asynchronously */
-void si4700_rds_read_raw_async(unsigned char *buf, int count); /* implemented by target */
-void si4700_rds_interrupt(void);
-#endif /* (CONFIG_RDS & RDS_CFG_ISR) */
-
-/* Read raw RDS info for processing */
+/* Read raw RDS info for processing.
+ * - If RDS_CFG_POLL is set, this function will read status and RDS data and process it if a new
+ * packet is available.
+ * - Otherwise this function will read a RDS packet and process it under the assumption that it is
+ * new. */
void si4700_rds_process(void);
#endif /* HAVE_RDS_CAP */
diff --git a/firmware/export/structec.h b/firmware/export/structec.h
deleted file mode 100644
index b3e7d69efa..0000000000
--- a/firmware/export/structec.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2007 by Miika Pekkarinen
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ****************************************************************************/
-
-#ifndef _STRUCTEC_H
-#define _STRUCTEC_H
-
-#include <sys/types.h>
-#include <stdbool.h>
-
-void structec_convert(void *structure, const char *ecinst,
- long count, bool enable);
-ssize_t ecread(int fd, void *buf, size_t scount, const char *ecinst, bool ec);
-ssize_t ecwrite(int fd, const void *buf, size_t scount, const char *ecinst, bool ec);
-#endif
-
diff --git a/firmware/export/system.h b/firmware/export/system.h
index f554ac7cf1..172fe05b6e 100644
--- a/firmware/export/system.h
+++ b/firmware/export/system.h
@@ -31,7 +31,13 @@ extern void system_reboot (void);
/* Called from any UIE handler and panicf - wait for a key and return
* to reboot system. */
extern void system_exception_wait(void);
+
+#if NUM_CORES == 1
+extern void system_init(void) INIT_ATTR;
+#else
+/* TODO: probably safe to use INIT_ATTR on multicore but this needs checking */
extern void system_init(void);
+#endif
extern long cpu_frequency;
@@ -258,7 +264,8 @@ static inline void cpu_boost_unlock(void)
#endif
/* Define this if target has support for generating backtraces */
-#ifdef CPU_ARM
+#if defined(CPU_ARM) || \
+ (defined(CPU_MIPS) && (CONFIG_PLATFORM & PLATFORM_NATIVE))
#define HAVE_RB_BACKTRACE
#endif
@@ -307,8 +314,8 @@ static inline void cpu_boost_unlock(void)
/* Define MEM_ALIGN_ATTR which may be used to align e.g. buffers for faster
* access. */
-#if defined(CPU_ARM)
- /* Use ARMs cache alignment. */
+#if defined(HAVE_CPU_CACHE_ALIGN)
+ /* Align to a cache line. */
#define MEM_ALIGN_ATTR CACHEALIGN_ATTR
#define MEM_ALIGN_SIZE CACHEALIGN_SIZE
#elif defined(CPU_COLDFIRE)
diff --git a/firmware/export/tcc77x.h b/firmware/export/tcc77x.h
deleted file mode 100644
index 3c457c5b38..0000000000
--- a/firmware/export/tcc77x.h
+++ /dev/null
@@ -1,262 +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.
- *
- ****************************************************************************/
-#ifndef __TCC77X_H__
-#define __TCC77X_H__
-
-#define CACHEALIGN_BITS (5)
-
-/* General-purpose IO */
-
-#define GPIOA (*(volatile unsigned long *)0x80000300)
-#define GPIOB (*(volatile unsigned long *)0x80000310)
-#define GPIOC (*(volatile unsigned long *)0x80000320)
-#define GPIOD (*(volatile unsigned long *)0x80000330)
-#define GPIOE (*(volatile unsigned long *)0x80000340)
-
-#define GPIOA_DIR (*(volatile unsigned long *)0x80000304)
-#define GPIOB_DIR (*(volatile unsigned long *)0x80000314)
-#define GPIOC_DIR (*(volatile unsigned long *)0x80000324)
-#define GPIOD_DIR (*(volatile unsigned long *)0x80000334)
-#define GPIOE_DIR (*(volatile unsigned long *)0x80000344)
-
-#define GPIOA_FUNC (*(volatile unsigned long *)0x80000308)
-#define GPIOB_FUNC (*(volatile unsigned long *)0x80000318)
-#define GPIOC_FUNC (*(volatile unsigned long *)0x80000328)
-#define GPIOD_FUNC (*(volatile unsigned long *)0x80000338)
-#define GPIOE_FUNC (*(volatile unsigned long *)0x80000348)
-
-#define BMI (*(volatile unsigned long *)0x80000364)
-
-/* Clock Generator */
-
-#define CLKCTRL (*(volatile unsigned long *)0x80000400)
-#define PLL0CFG (*(volatile unsigned long *)0x80000404)
-#define CLKDIVC (*(volatile unsigned long *)0x8000040c)
-#define MODECTR (*(volatile unsigned long *)0x80000410)
-#define BCLKCTR (*(volatile unsigned long *)0x80000414)
-#define SWRESET (*(volatile unsigned long *)0x80000418)
-#define PCLKCFG0 (*(volatile unsigned long *)0x8000041c)
-#define PCLKCFG1 (*(volatile unsigned long *)0x80000420)
-#define PCLKCFG2 (*(volatile unsigned long *)0x80000424)
-#define PCLKCFG3 (*(volatile unsigned long *)0x80000428)
-#define PCLKCFG4 (*(volatile unsigned long *)0x8000042c)
-#define PCLKCFG5 (*(volatile unsigned long *)0x80000430)
-#define PCLKCFG6 (*(volatile unsigned long *)0x80000434)
-
-#define PCLK_DAI PCLKCFG6
-
-/* Device bits for SWRESET & BCLKCTR */
-
-#define DEV_DAI (1<<0)
-#define DEV_USBD (1<<4)
-#define DEV_ECC (1<<9)
-#define DEV_NAND (1<<16)
-
-/* ADC */
-
-#define ADCCON (*(volatile unsigned long *)0x80000a00)
-#define ADCDATA (*(volatile unsigned long *)0x80000a04)
-#define ADCCONA (*(volatile unsigned long *)0x80000a80)
-#define ADCSTATUS (*(volatile unsigned long *)0x80000a84)
-#define ADCCFG (*(volatile unsigned long *)0x80000a88)
-
-
-/* Memory Controller */
-#define SDCFG (*(volatile unsigned long *)0xf0000000)
-#define SDFSM (*(volatile unsigned long *)0xf0000004)
-#define MCFG (*(volatile unsigned long *)0xf0000008)
-#define TST (*(volatile unsigned long *)0xf000000c)
-#define CSCFG0 (*(volatile unsigned long *)0xf0000010)
-#define CSCFG1 (*(volatile unsigned long *)0xf0000014)
-#define CSCFG2 (*(volatile unsigned long *)0xf0000018)
-#define CSCFG3 (*(volatile unsigned long *)0xf000001c)
-#define CLKCFG (*(volatile unsigned long *)0xf0000020)
-#define SDCMD (*(volatile unsigned long *)0xf0000024)
-
-
-/* IRQ Controller */
-#define EXT0_IRQ_MASK (1<<0)
-#define EXT1_IRQ_MASK (1<<1)
-#define EXT2_IRQ_MASK (1<<2)
-#define EXT3_IRQ_MASK (1<<3)
-#define I2SR_IRQ_MASK (1<<4)
-#define I2ST_IRQ_MASK (1<<5)
-#define TIMER0_IRQ_MASK (1<<6)
-#define USBD_IRQ_MASK (1<<8) /* USB 2.0 device */
-#define USBH_IRQ_MASK (1<<10) /* USB 1.1 host */
-#define ADC_IRQ_MASK (1<<16)
-#define USB_DMA_IRQ_MASK (1<<26) /* USB DMA */
-#define ECC_IRQ_MASK (1<<27)
-
-#define DAI_RX_IRQ_MASK I2SR_IRQ_MASK
-#define DAI_TX_IRQ_MASK I2ST_IRQ_MASK
-
-#define USB_DMA_IRQ_MASK (1<<26) /* USB DMA */
-
-#define IEN (*(volatile unsigned long *)0x80000100)
-#define CREQ (*(volatile unsigned long *)0x80000104)
-#define IREQ (*(volatile unsigned long *)0x80000108)
-#define IRQSEL (*(volatile unsigned long *)0x8000010c)
-#define ICFG (*(volatile unsigned long *)0x80000110)
-#define MREQ (*(volatile unsigned long *)0x80000114)
-#define TSTREQ (*(volatile unsigned long *)0x80000118)
-#define IRQ (*(volatile unsigned long *)0x80000120)
-#define FIQ (*(volatile unsigned long *)0x80000124)
-#define MIRQ (*(volatile unsigned long *)0x80000128)
-#define MFIQ (*(volatile unsigned long *)0x8000012c)
-#define TMODE (*(volatile unsigned long *)0x80000130)
-#define SYNC (*(volatile unsigned long *)0x80000134)
-#define WKUP (*(volatile unsigned long *)0x80000138)
-
-/* Timer Controller */
-
-#define TCFG0 (*(volatile unsigned long *)0x80000200)
-#define TCNT0 (*(volatile unsigned long *)0x80000204)
-#define TREF0 (*(volatile unsigned long *)0x80000208)
-#define TMREF0 (*(volatile unsigned long *)0x8000020c)
-#define TCFG1 (*(volatile unsigned long *)0x80000210)
-#define TCNT1 (*(volatile unsigned long *)0x80000214)
-#define TREF1 (*(volatile unsigned long *)0x80000218)
-#define TMREF1 (*(volatile unsigned long *)0x8000021c)
-#define TCFG2 (*(volatile unsigned long *)0x80000220)
-#define TCNT2 (*(volatile unsigned long *)0x80000224)
-#define TREF2 (*(volatile unsigned long *)0x80000228)
-#define TMREF2 (*(volatile unsigned long *)0x8000022c)
-#define TCFG3 (*(volatile unsigned long *)0x80000230)
-#define TCNT3 (*(volatile unsigned long *)0x80000234)
-#define TREF3 (*(volatile unsigned long *)0x80000238)
-#define TMREF3 (*(volatile unsigned long *)0x8000023c)
-#define TCFG4 (*(volatile unsigned long *)0x80000240)
-#define TCNT4 (*(volatile unsigned long *)0x80000244)
-#define TREF4 (*(volatile unsigned long *)0x80000248)
-#define TCFG5 (*(volatile unsigned long *)0x80000250)
-#define TCNT5 (*(volatile unsigned long *)0x80000254)
-#define TREF5 (*(volatile unsigned long *)0x80000258)
-#define TIREQ (*(volatile unsigned long *)0x80000260)
-#define TWDCFG (*(volatile unsigned long *)0x80000270)
-#define TWDCLR (*(volatile unsigned long *)0x80000274)
-#define TC32EN (*(volatile unsigned long *)0x80000280)
-#define TC32LDV (*(volatile unsigned long *)0x80000284)
-#define TC32CMP0 (*(volatile unsigned long *)0x80000288)
-#define TC32CMP1 (*(volatile unsigned long *)0x8000028c)
-#define TC32PCNT (*(volatile unsigned long *)0x80000290)
-#define TC32MCNT (*(volatile unsigned long *)0x80000294)
-#define TC32IRQ (*(volatile unsigned long *)0x80000298)
-
-/* TIREQ flags */
-#define TF0 (1<<8) /* Timer 0 reference value reached */
-#define TF1 (1<<9) /* Timer 1 reference value reached */
-#define TI0 (1<<0) /* Timer 0 IRQ flag */
-#define TI1 (1<<1) /* Timer 1 IRQ flag */
-
-/* NAND Flash Controller */
-
-#define NFC_CMD (*(volatile unsigned long *)0x90000000)
-#define NFC_SADDR (*(volatile unsigned long *)0x9000000C)
-#define NFC_SDATA (*(volatile unsigned long *)0x90000040)
-#define NFC_WDATA (*(volatile unsigned long *)0x90000010)
-#define NFC_CTRL (*(volatile unsigned long *)0x90000050)
- #define NFC_16BIT (1<<26)
- #define NFC_CS0 (1<<23)
- #define NFC_CS1 (1<<22)
- #define NFC_READY (1<<20)
-#define NFC_IREQ (*(volatile unsigned long *)0x90000060)
-#define NFC_RST (*(volatile unsigned long *)0x90000064)
-
-
-/* ECC controller */
-
-#define ECC_CTRL (*(volatile unsigned long *)0x80000900)
- #define ECC_DMA_REQ (1<<28)
- #define ECC_ENC (1<<27) /* MLC ECC3/4 */
- #define ECC_READY (1<<26)
- #define ECC_IEN (1<<25)
- #define ECC_MANUAL (1<<22)
- #define ECC_WCNT (1<<12) /* [21:12] */
- #define ECC_HOLD (1<<7)
- #define ECC_M4EN (1<<6)
- #define ECC_ZERO (1<<5)
- #define ECC_M3EN (1<<4)
- #define ECC_CNT_MASK (7<<1)
- #define ECC_CNT (1<<1)
- #define ECC_SLC (1<<0)
-
-#define ECC_BASE (*(volatile unsigned long *)0x80000904)
-#define ECC_MASK (*(volatile unsigned long *)0x80000908)
-#define ECC_CLR (*(volatile unsigned long *)0x8000090c)
-#define SLC_ECC0 (*(volatile unsigned long *)0x80000910)
-#define SLC_ECC1 (*(volatile unsigned long *)0x80000914)
-#define SLC_ECC2 (*(volatile unsigned long *)0x80000918)
-#define SLC_ECC3 (*(volatile unsigned long *)0x8000091c)
-#define SLC_ECC4 (*(volatile unsigned long *)0x80000920)
-#define SLC_ECC5 (*(volatile unsigned long *)0x80000924)
-#define SLC_ECC6 (*(volatile unsigned long *)0x80000928)
-#define SLC_ECC7 (*(volatile unsigned long *)0x8000092c)
-#define MLC_ECC0W (*(volatile unsigned long *)0x80000930)
-#define MLC_ECC1W (*(volatile unsigned long *)0x80000934)
-#define MLC_ECC2W (*(volatile unsigned long *)0x80000938)
-#define MLC_ECC0R (*(volatile unsigned long *)0x80000940)
-#define MLC_ECC1R (*(volatile unsigned long *)0x80000944)
-#define MLC_ECC2R (*(volatile unsigned long *)0x80000948)
-#define ECC_CORR_START (*(volatile unsigned long *)0x8000094c)
-#define ECC_ERRADDR(x) (*(volatile unsigned long *)(0x80000950+4*(x)))
-#define ECC_ERRDATA(x) (*(volatile unsigned long *)(0x80000960+4*(x)))
-#define ECC_ERR_NUM (*(volatile unsigned long *)0x80000970)
-
-
-/* Digital Audio Interface */
-#define DADI_L0 (*(volatile unsigned long *)0x80000000)
-#define DADI_R0 (*(volatile unsigned long *)0x80000004)
-#define DADI_L1 (*(volatile unsigned long *)0x80000008)
-#define DADI_R1 (*(volatile unsigned long *)0x8000000C)
-#define DADI_L2 (*(volatile unsigned long *)0x80000010)
-#define DADI_R2 (*(volatile unsigned long *)0x80000014)
-#define DADI_L3 (*(volatile unsigned long *)0x80000018)
-#define DADI_R3 (*(volatile unsigned long *)0x8000001c)
-
-#define DADO_L0 (*(volatile unsigned long *)0x80000020)
-#define DADO_R0 (*(volatile unsigned long *)0x80000024)
-#define DADO_L1 (*(volatile unsigned long *)0x80000028)
-#define DADO_R1 (*(volatile unsigned long *)0x8000002C)
-#define DADO_L2 (*(volatile unsigned long *)0x80000030)
-#define DADO_R2 (*(volatile unsigned long *)0x80000034)
-#define DADO_L3 (*(volatile unsigned long *)0x80000038)
-#define DADO_R3 (*(volatile unsigned long *)0x8000003c)
-
-#define DAMR (*(volatile unsigned long *)0x80000040)
-#define DAVC (*(volatile unsigned long *)0x80000044)
-
-#define DADI_L(x) (*(volatile unsigned long *)(0x80000000 + (x) * 8))
-#define DADI_R(x) (*(volatile unsigned long *)(0x80000004 + (x) * 8))
-#define DADO_L(x) (*(volatile unsigned long *)(0x80000020 + (x) * 8))
-#define DADO_R(x) (*(volatile unsigned long *)(0x80000024 + (x) * 8))
-
-/* USB 2.0 device system MMR base address */
-#define USB_BASE 0x90000b00
-
-#define USB_NUM_ENDPOINTS 3
-#define USB_DEVBSS_ATTR IBSS_ATTR
-
-/* Timer frequency */
-/* timers are based on XIN (12Mhz) */
-#define TIMER_FREQ (12000000)
-
-#endif
diff --git a/firmware/export/ucl_decompress.h b/firmware/export/ucl_decompress.h
new file mode 100644
index 0000000000..bea7564a94
--- /dev/null
+++ b/firmware/export/ucl_decompress.h
@@ -0,0 +1,32 @@
+/* Standalone UCL decompressor and uclpack-compatible unpacker,
+ * adapted for Rockbox from the libucl code.
+ *
+ * Copyright (C) 1996-2002 Markus Franz Xaver Johannes Oberhumer
+ * All Rights Reserved.
+ *
+ * See firmware/common/ucl_decompress.c for full copyright notice
+ */
+#ifndef UCL_DECOMPRESS_H
+#define UCL_DECOMPRESS_H
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#define UCL_E_OK 0
+#define UCL_E_INPUT_OVERRUN 1
+#define UCL_E_OUTPUT_OVERRUN 2
+#define UCL_E_LOOKBEHIND_OVERRUN 3
+#define UCL_E_INPUT_NOT_CONSUMED 4
+#define UCL_E_BAD_MAGIC 5
+#define UCL_E_BAD_BLOCK_SIZE 6
+#define UCL_E_UNSUPPORTED_METHOD 7
+#define UCL_E_TRUNCATED 8
+#define UCL_E_CORRUPTED 9
+
+extern int ucl_nrv2e_decompress_8(const uint8_t* src, uint32_t src_len,
+ uint8_t* dst, uint32_t* dst_len);
+
+extern int ucl_unpack(const uint8_t* src, uint32_t src_len,
+ uint8_t* dst, uint32_t* dst_len);
+
+#endif /* UCL_DECOMPRESS_H */
diff --git a/firmware/export/usb-designware.h b/firmware/export/usb-designware.h
index 428733b4f5..9e5496f0db 100644
--- a/firmware/export/usb-designware.h
+++ b/firmware/export/usb-designware.h
@@ -216,8 +216,11 @@
#define DWC_DIEPINT(x) (*((REG32_PTR_T)(OTGBASE + 0x908 + 0x20*(x))))
#define DWC_DOEPINT(x) (*((REG32_PTR_T)(OTGBASE + 0xb08 + 0x20*(x))))
+ #define SETUPRECVD (1<<15) /* control OUT */
#define TXFE (1<<7) /* IN */
+ #define BACK2BACKSETUP (1<<6) /* control OUT */
#define INEPNE (1<<6) /* IN */
+ #define STATUSRECVD (1<<5) /* control OUT */
#define ITEPMIS (1<<5) /* IN */
#define ITTXFE (1<<4) /* IN */
#define OTEPDIS (1<<4) /* OUT */
diff --git a/firmware/export/usb.h b/firmware/export/usb.h
index 2bcd95ef81..c075fa83ec 100644
--- a/firmware/export/usb.h
+++ b/firmware/export/usb.h
@@ -134,7 +134,6 @@ enum
/* Supported usb modes. */
enum
{
- USB_MODE_ASK = 0,
USB_MODE_MASS_STORAGE,
USB_MODE_CHARGE,
USB_MODE_ADB
@@ -178,7 +177,7 @@ struct usb_transfer_completion_event_data
int dir;
int status;
int length;
- void* data;
+ void* data[2];
};
#endif /* HAVE_USBSTACK */
diff --git a/firmware/export/usb_ch9.h b/firmware/export/usb_ch9.h
index c11775b893..659bcca101 100644
--- a/firmware/export/usb_ch9.h
+++ b/firmware/export/usb_ch9.h
@@ -391,6 +391,23 @@ struct usb_debug_descriptor {
} __attribute__((packed));
/*-------------------------------------------------------------------------*/
+
+/* USB_DT_INTERFACE_ASSOCIATION: groups interfaces */
+struct usb_interface_assoc_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+
+ uint8_t bFirstInterface;
+ uint8_t bInterfaceCount;
+ uint8_t bFunctionClass;
+ uint8_t bFunctionSubClass;
+ uint8_t bFunctionProtocol;
+ uint8_t iFunction;
+} __attribute__ ((packed));
+
+#define USB_DT_INTERFACE_ASSOCIATION_SIZE 8
+
+/*-------------------------------------------------------------------------*/
/* USB 2.0 defines three speeds, here's how Linux identifies them */
enum usb_device_speed {
diff --git a/firmware/export/usb_core.h b/firmware/export/usb_core.h
index 75fafc06a8..fe1f7459cf 100644
--- a/firmware/export/usb_core.h
+++ b/firmware/export/usb_core.h
@@ -39,11 +39,21 @@
extern int usb_max_pkt_size;
+enum {
+ USB_STRING_INDEX_LANGUAGE,
+ USB_STRING_INDEX_MANUFACTURER,
+ USB_STRING_INDEX_PRODUCT,
+ USB_STRING_INDEX_SERIAL,
+ USB_STRING_INDEX_MAX,
+};
+
struct usb_class_driver;
void usb_core_init(void);
void usb_core_exit(void);
-void usb_core_control_request(struct usb_ctrlrequest* req);
+void usb_core_control_request(struct usb_ctrlrequest* req, void* data);
+void usb_core_control_complete(int status);
+void usb_core_legacy_control_request(struct usb_ctrlrequest* req);
void usb_core_transfer_complete(int endpoint,int dir,int status,int length);
void usb_core_bus_reset(void);
bool usb_core_any_exclusive_storage(void);
diff --git a/firmware/export/usb_drv.h b/firmware/export/usb_drv.h
index 7ef7c8b7ee..3ef4db3c9c 100644
--- a/firmware/export/usb_drv.h
+++ b/firmware/export/usb_drv.h
@@ -56,6 +56,12 @@
* -> usb_drv_int_enable(false) [ditto]
* -> soc specific controller/clock deinit */
+enum usb_control_response {
+ USB_CONTROL_ACK,
+ USB_CONTROL_STALL,
+ USB_CONTROL_RECEIVE,
+};
+
/* one-time initialisation of the USB driver */
void usb_drv_startup(void);
void usb_drv_int_enable(bool enable); /* Target implemented */
@@ -68,8 +74,9 @@ void usb_drv_stall(int endpoint, bool stall,bool in);
bool usb_drv_stalled(int endpoint,bool in);
int usb_drv_send(int endpoint, void* ptr, int length);
int usb_drv_send_nonblocking(int endpoint, void* ptr, int length);
-int usb_drv_recv(int endpoint, void* ptr, int length);
-void usb_drv_ack(struct usb_ctrlrequest* req);
+int usb_drv_recv_nonblocking(int endpoint, void* ptr, int length);
+void usb_drv_control_response(enum usb_control_response resp,
+ void* data, int length);
void usb_drv_set_address(int address);
void usb_drv_reset_endpoint(int endpoint, bool send);
bool usb_drv_powered(void);
diff --git a/firmware/target/arm/pnx0101/i2c-pnx0101.c b/firmware/export/wifi.h
index eaf1e79500..a70e062cb0 100644
--- a/firmware/target/arm/pnx0101/i2c-pnx0101.c
+++ b/firmware/export/wifi.h
@@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Copyright (C) 2005 by Tomasz Malesinski
+ * Copyright (C) 2021 by Tomasz Moń
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -19,6 +19,11 @@
*
****************************************************************************/
-void i2c_init(void)
-{
-}
+#ifndef WIFI_H
+#define WIFI_H
+
+#include "config.h"
+
+void wifi_init(void) INIT_ATTR;
+
+#endif
diff --git a/firmware/export/x1000-codec.h b/firmware/export/x1000-codec.h
new file mode 100644
index 0000000000..cfc71dbd60
--- /dev/null
+++ b/firmware/export/x1000-codec.h
@@ -0,0 +1,184 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021-2022 Aidan MacDonald
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef __X1000_CODEC_H__
+#define __X1000_CODEC_H__
+
+#include "config.h"
+#include <stdbool.h>
+
+/* Note: the internal X1000 codec supports playback and record, but devices
+ * can employ an external codec for one and the internal codec for the other.
+ * The caveat, in this case, is that only one codec can be used at a time
+ * because the HW cannot mux playback/record independently.
+ *
+ * At present only recording is implemented, since all X1000 ports use an
+ * external DAC for playback.
+ */
+
+#ifdef HAVE_X1000_ICODEC_PLAY
+# error "X1000 icodec playback not implemented"
+#endif
+
+#define X1000_ICODEC_ADC_GAIN_MIN 0
+#define X1000_ICODEC_ADC_GAIN_MAX 43
+#define X1000_ICODEC_ADC_GAIN_STEP 1
+
+#define X1000_ICODEC_MIC_GAIN_MIN 0
+#define X1000_ICODEC_MIC_GAIN_MAX 20
+#define X1000_ICODEC_MIC_GAIN_STEP 4
+
+#ifdef HAVE_X1000_ICODEC_REC
+AUDIOHW_SETTING(MIC_GAIN, "dB", 0, 1, 0, 63, 12)
+#endif
+
+#define JZCODEC_INDIRECT_CREG(r) ((r) & 0xff)
+#define JZCODEC_INDIRECT_INDEX(r) (((r) >> 8) & 0x7)
+#define JZCODEC_INDIRECT_BIT 0x800
+
+#define JZCODEC_INDIRECT(c, i) (JZCODEC_INDIRECT_BIT | ((i) << 8) | (c))
+
+/* Codec registers from Ingenic's kernel sources. The datasheet is badly
+ * screwed up and the addresses listed cannot be trusted. */
+enum {
+ JZCODEC_SR = 0,
+ JZCODEC_SR2,
+ JZCODEC_SIGR,
+ JZCODEC_SIGR2,
+ JZCODEC_SIGR3,
+ JZCODEC_SIGR5,
+ JZCODEC_SIGR7,
+ JZCODEC_MR,
+ JZCODEC_AICR_DAC,
+ JZCODEC_AICR_ADC,
+ JZCODEC_CR_DMIC,
+ JZCODEC_CR_MIC1,
+ JZCODEC_CR_MIC2,
+ JZCODEC_CR_DAC,
+ JZCODEC_CR_DAC2,
+ JZCODEC_CR_ADC,
+ JZCODEC_CR_MIX,
+ JZCODEC_DR_MIX,
+ JZCODEC_CR_VIC,
+ JZCODEC_CR_CK,
+ JZCODEC_FCR_DAC,
+ JZCODEC_SFCCR_DAC,
+ JZCODEC_SFFCR_DAC,
+ JZCODEC_FCR_ADC,
+ JZCODEC_CR_TIMER_MSB,
+ JZCODEC_CR_TIMER_LSB,
+ JZCODEC_ICR,
+ JZCODEC_IMR,
+ JZCODEC_IFR,
+ JZCODEC_IMR2,
+ JZCODEC_IFR2,
+ JZCODEC_GCR_DACL,
+ JZCODEC_GCR_DACR,
+ JZCODEC_GCR_DACL2,
+ JZCODEC_GCR_DACR2,
+ JZCODEC_GCR_MIC1,
+ JZCODEC_GCR_MIC2,
+ JZCODEC_GCR_ADCL,
+ JZCODEC_GCR_ADCR,
+ JZCODEC_GCR_MIXDACL,
+ JZCODEC_GCR_MIXDACR,
+ JZCODEC_GCR_MIXADCL,
+ JZCODEC_GCR_MIXADCR,
+ JZCODEC_CR_DAC_AGC,
+ JZCODEC_DR_DAC_AGC,
+ JZCODEC_CR_DAC2_AGC,
+ JZCODEC_DR_DAC2_AGC,
+ JZCODEC_CR_ADC_AGC,
+ JZCODEC_DR_ADC_AGC,
+ JZCODEC_SR_ADC_AGCDGL,
+ JZCODEC_SR_ADC_AGCDGR,
+ JZCODEC_SR_ADC_AGCAGL,
+ JZCODEC_SR_ADC_AGCAGR,
+ JZCODEC_CR_TR,
+ JZCODEC_DR_TR,
+ JZCODEC_SR_TR1,
+ JZCODEC_SR_TR2,
+ JZCODEC_SR_TR_SRCDAC,
+
+ JZCODEC_MIX0 = JZCODEC_INDIRECT(JZCODEC_CR_MIX, 0),
+ JZCODEC_MIX1 = JZCODEC_INDIRECT(JZCODEC_CR_MIX, 1),
+ JZCODEC_MIX2 = JZCODEC_INDIRECT(JZCODEC_CR_MIX, 2),
+ JZCODEC_MIX3 = JZCODEC_INDIRECT(JZCODEC_CR_MIX, 3),
+ JZCODEC_MIX4 = JZCODEC_INDIRECT(JZCODEC_CR_MIX, 4),
+
+ JZCODEC_DAC_AGC0 = JZCODEC_INDIRECT(JZCODEC_CR_DAC_AGC, 0),
+ JZCODEC_DAC_AGC1 = JZCODEC_INDIRECT(JZCODEC_CR_DAC_AGC, 1),
+ JZCODEC_DAC_AGC2 = JZCODEC_INDIRECT(JZCODEC_CR_DAC_AGC, 2),
+ JZCODEC_DAC_AGC3 = JZCODEC_INDIRECT(JZCODEC_CR_DAC_AGC, 3),
+
+ JZCODEC_DAC2_AGC0 = JZCODEC_INDIRECT(JZCODEC_CR_DAC2_AGC, 0),
+ JZCODEC_DAC2_AGC1 = JZCODEC_INDIRECT(JZCODEC_CR_DAC2_AGC, 1),
+ JZCODEC_DAC2_AGC2 = JZCODEC_INDIRECT(JZCODEC_CR_DAC2_AGC, 2),
+ JZCODEC_DAC2_AGC3 = JZCODEC_INDIRECT(JZCODEC_CR_DAC2_AGC, 3),
+
+ JZCODEC_ADC_AGC0 = JZCODEC_INDIRECT(JZCODEC_CR_ADC_AGC, 0),
+ JZCODEC_ADC_AGC1 = JZCODEC_INDIRECT(JZCODEC_CR_ADC_AGC, 1),
+ JZCODEC_ADC_AGC2 = JZCODEC_INDIRECT(JZCODEC_CR_ADC_AGC, 2),
+ JZCODEC_ADC_AGC3 = JZCODEC_INDIRECT(JZCODEC_CR_ADC_AGC, 3),
+ JZCODEC_ADC_AGC4 = JZCODEC_INDIRECT(JZCODEC_CR_ADC_AGC, 4),
+};
+
+/* for use with x1000_icodec_mic1_configure() */
+enum {
+ JZCODEC_MIC1_SINGLE_ENDED = (0 << 6),
+ JZCODEC_MIC1_DIFFERENTIAL = (1 << 6),
+
+ JZCODEC_MIC1_BIAS_2_08V = (0 << 3),
+ JZCODEC_MIC1_BIAS_1_66V = (1 << 3),
+
+ JZCODEC_MIC1_CONFIGURE_MASK = (1 << 6) | (1 << 3),
+};
+
+/* for use with x1000_icodec_adc_mic_sel() */
+enum {
+ JZCODEC_MIC_SEL_ANALOG,
+ JZCODEC_MIC_SEL_DIGITAL,
+};
+
+extern void x1000_icodec_open(void);
+extern void x1000_icodec_close(void);
+
+extern void x1000_icodec_dac_frequency(int fsel);
+
+extern void x1000_icodec_adc_enable(bool en);
+extern void x1000_icodec_adc_mute(bool muted);
+extern void x1000_icodec_adc_mic_sel(int sel);
+extern void x1000_icodec_adc_frequency(int fsel);
+extern void x1000_icodec_adc_highpass_filter(bool en);
+extern void x1000_icodec_adc_gain(int gain_dB);
+
+extern void x1000_icodec_mic1_enable(bool en);
+extern void x1000_icodec_mic1_bias_enable(bool en);
+extern void x1000_icodec_mic1_configure(int settings);
+extern void x1000_icodec_mic1_gain(int gain_dB);
+
+extern void x1000_icodec_mixer_enable(bool en);
+
+extern int x1000_icodec_read(int reg);
+extern void x1000_icodec_write(int reg, int value);
+extern void x1000_icodec_update(int reg, int mask, int value);
+
+#endif /* __X1000_CODEC_H__ */
diff --git a/firmware/export/x1000.h b/firmware/export/x1000.h
new file mode 100644
index 0000000000..b71d37d64d
--- /dev/null
+++ b/firmware/export/x1000.h
@@ -0,0 +1,124 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021-2022 Aidan MacDonald
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef __X1000_H__
+#define __X1000_H__
+
+#include "config.h"
+
+/* Frequency of external oscillator EXCLK */
+//#define X1000_EXCLK_FREQ 24000000
+
+/* Maximum CPU frequency that can be achieved on the target */
+//#define CPU_FREQ 1008000000
+
+/* Only 24 MHz and 26 MHz external oscillators are supported by the X1000 */
+#if X1000_EXCLK_FREQ == 24000000
+# define X1000_EXCLK_24MHZ
+#elif X1000_EXCLK_FREQ == 26000000
+# define X1000_EXCLK_26MHZ
+#else
+# error "Unsupported EXCLK freq"
+#endif
+
+/* On-chip TCSM (tightly coupled shared memory), aka IRAM. The SPL runs from
+ * here, but the rest of Rockbox doesn't use it - it is too difficult to use
+ * as a normal memory region because it's not in KSEG0. */
+#define X1000_TCSM_BASE 0xf4000000
+#define X1000_TCSM_SIZE (16 * 1024)
+
+/* SPL load and entry point addresses, this is defined by the HW boot ROM.
+ * First 4K is used by mask ROM for stack + variables, and the next 2K are
+ * occupied by SPL header. Usable code+data size is 10K. */
+#define X1000_SPL_LOAD_ADDR (X1000_TCSM_BASE + 0x1000)
+#define X1000_SPL_EXEC_ADDR (X1000_TCSM_BASE + 0x1800)
+#define X1000_SPL_SIZE (X1000_TCSM_SIZE - 0x1800)
+
+/* External SDRAM - just one big linear mapping in KSEG0. */
+#define X1000_SDRAM_BASE 0x80000000
+#define X1000_SDRAM_SIZE (MEMORYSIZE * 1024 * 1024)
+#define X1000_SDRAM_END (X1000_SDRAM_BASE + X1000_SDRAM_SIZE)
+
+/* Memory definitions for Rockbox
+ *
+ * IRAM - Contains the exception handlers and acts as a safe stub area
+ * from which you can overwrite the rest of DRAM (used by RoLo).
+ *
+ * DRAM - This is the main RAM area used for code, data, and bss sections.
+ * The audio, codec, and plugin buffers also reside in here.
+ *
+ * X1000_IRAM_BASE is the base of the exception vectors and must be set to
+ * the base of kseg0 (0x80000000). The X1000 supports the EBase register so
+ * the vectors can be remapped, allowing IRAM to be moved to any 4K-aligned
+ * address, but it would introduce more complexity and there's currently no
+ * good reason to do this.
+ *
+ * X1000_DRAM_BASE doubles as the entry point address. There is some legacy
+ * baggage surrounding this value so be careful when changing it.
+ *
+ * - Rockbox's DRAM_BASE should always equal X1000_STANDARD_DRAM_BASE because
+ * this value is hardcoded by old bootloaders released in 2021. This can be
+ * changed if truly necessary, but it should be avoided.
+ * - The bootloader's DRAM_BASE can be changed freely but if it isn't equal
+ * to X1000_STANDARD_DRAM_BASE, the update package generation *must* be
+ * updated to use the "bootloader2.ucl" filename to ensure old jztools do
+ * not try to incorrectly boot the binary at the wrong load address.
+ *
+ * The bootloader DRAM_BASE is also hardcoded in the SPL, but the SPL is
+ * considered as part of the bootloader to avoid introducing unnecessary
+ * ABI boundaries. Therefore this hardcoded use can safely be ignored.
+ *
+ * There is no requirement that IRAM and DRAM are contiguous, but they must
+ * reside in the same segment (ie. upper 3 address bits must be identical),
+ * otherwise we need long calls to go between the two.
+ */
+#define X1000_IRAM_BASE X1000_SDRAM_BASE
+#define X1000_IRAM_SIZE (16 * 1024)
+#define X1000_IRAM_END (X1000_IRAM_BASE + X1000_IRAM_SIZE)
+#define X1000_DRAM_BASE X1000_IRAM_END
+#define X1000_DRAM_SIZE (X1000_SDRAM_SIZE - X1000_IRAM_SIZE)
+#define X1000_DRAM_END (X1000_DRAM_BASE + X1000_DRAM_SIZE)
+
+/* Stacks are placed in IRAM to avoid various annoying issues in boot code. */
+#define X1000_STACKSIZE 0x1e00
+#define X1000_IRQSTACKSIZE 0x300
+
+/* Standard DRAM base address for backward compatibility */
+#define X1000_STANDARD_DRAM_BASE 0x80004000
+
+/* Required for pcm_rec_dma_get_peak_buffer(), doesn't do anything
+ * except on targets with recording. */
+#define HAVE_PCM_DMA_ADDRESS
+#define HAVE_PCM_REC_DMA_ADDRESS
+
+/* Convert kseg0 address to physical address or uncached address */
+#define PHYSADDR(x) ((unsigned long)(x) & 0x1fffffff)
+#define UNCACHEDADDR(x) (PHYSADDR(x) | 0xa0000000)
+
+/* Defines for usb-designware driver */
+#define OTGBASE 0xb3500000
+#define USB_NUM_ENDPOINTS 9
+
+/* CPU cache parameters */
+#define CACHEALIGN_BITS 5
+#define CACHE_SIZE (16 * 1024)
+
+#endif /* __X1000_H__ */
diff --git a/firmware/export/xduoolinux_codec.h b/firmware/export/xduoolinux_codec.h
index 301a341f15..a5bdab0661 100644
--- a/firmware/export/xduoolinux_codec.h
+++ b/firmware/export/xduoolinux_codec.h
@@ -3,7 +3,6 @@
#define AUDIOHW_CAPS (LINEOUT_CAP | FILTER_ROLL_OFF_CAP)
AUDIOHW_SETTING(VOLUME, "dB", 1, 5, -102*10, 0, -30*10)
-AUDIOHW_SETTING(FILTER_ROLL_OFF, "", 0, 1, 0, 4, 0)
#endif
// We want this, but the codec takes over a second to unmute!
@@ -12,10 +11,14 @@ AUDIOHW_SETTING(FILTER_ROLL_OFF, "", 0, 1, 0, 4, 0)
#if defined(XDUOO_X3II)
/* The AK4490 glitches when switching sample rates */
#define AUDIOHW_MUTE_ON_SRATE_CHANGE
+AUDIOHW_SETTING(FILTER_ROLL_OFF, "", 0, 1, 0, 5, 0)
+#define AUDIOHW_HAVE_SS_ROLL_OFF
#endif
#if defined(XDUOO_X20)
//#define AUDIOHW_NEEDS_INITIAL_UNMUTE
+AUDIOHW_SETTING(FILTER_ROLL_OFF, "", 0, 1, 0, 4, 0)
+#define AUDIOHW_HAVE_SHORT2_ROLL_OFF
#endif
void audiohw_mute(int mute);
diff --git a/firmware/font.c b/firmware/font.c
index b8fa1c537f..baaec13d3c 100644
--- a/firmware/font.c
+++ b/firmware/font.c
@@ -44,8 +44,15 @@
#include "diacritic.h"
#include "rbpaths.h"
+/* Define LOGF_ENABLE to enable logf output in this file */
+//#define LOGF_ENABLE
+#include "logf.h"
+
#define MAX_FONTSIZE_FOR_16_BIT_OFFSETS 0xFFDB
+#define FONT_EXT "fnt"
+#define GLYPH_CACHE_EXT "gc"
+
/* max static loadable font buffer size */
#ifndef MAX_FONT_SIZE
#if LCD_HEIGHT > 64
@@ -90,7 +97,8 @@ extern struct font sysfont;
struct buflib_alloc_data {
struct font font; /* must be the first member! */
- int handle_locks; /* is the buflib handle currently locked? */
+ char *path; /* font path and filename (allocd at end of buffer) */
+ size_t path_bufsz; /* size of path buffer */
int refcount; /* how many times has this font been loaded? */
unsigned char buffer[];
};
@@ -107,9 +115,6 @@ static int buflibmove_callback(int handle, void* current, void* new)
struct buflib_alloc_data *alloc = (struct buflib_alloc_data*)current;
ptrdiff_t diff = new - current;
- if (alloc->handle_locks > 0)
- return BUFLIB_CB_CANNOT_MOVE;
-
#define UPDATE(x) if (x) { x = PTR_ADD(x, diff); }
UPDATE(alloc->font.bits);
@@ -119,40 +124,34 @@ static int buflibmove_callback(int handle, void* current, void* new)
UPDATE(alloc->font.buffer_start);
UPDATE(alloc->font.buffer_end);
UPDATE(alloc->font.buffer_position);
+ UPDATE(alloc->path);
UPDATE(alloc->font.cache._index);
UPDATE(alloc->font.cache._lru._base);
-
+ logf("%s %s", __func__, alloc->path);
return BUFLIB_CB_OK;
}
static void lock_font_handle(int handle, bool lock)
{
if ( handle < 0 )
return;
- struct buflib_alloc_data *alloc = core_get_data(handle);
- if ( lock )
- alloc->handle_locks++;
+
+ if (lock)
+ core_pin(handle);
else
- alloc->handle_locks--;
+ core_unpin(handle);
}
void font_lock(int font_id, bool lock)
{
if( font_id < 0 || font_id >= MAXFONTS )
return;
- if( buflib_allocations[font_id] >= 0 )
+ if( buflib_allocations[font_id] > 0 )
lock_font_handle(buflib_allocations[font_id], lock);
}
static struct buflib_callbacks buflibops = {buflibmove_callback, NULL, NULL };
-static inline struct font *pf_from_handle(int handle)
-{
- struct buflib_alloc_data *alloc = core_get_data(handle);
- struct font *pf = &alloc->font;
- return pf;
-}
-
static inline unsigned char *buffer_from_handle(int handle)
{
struct buflib_alloc_data *alloc = core_get_data(handle);
@@ -327,31 +326,36 @@ static struct font* font_load_cached(struct font* pf,
return pf;
}
+bool font_filename_matches_loaded_id(int font_id, const char *filename)
+{
+ if ( font_id >= 0 && font_id < MAXFONTS )
+ {
+ int handle = buflib_allocations[font_id];
+ if (handle > 0)
+ {
+ struct buflib_alloc_data *data = core_get_data(handle);
+ logf("%s id: [%d], %s", __func__, font_id, data->path);
+ return strcmp(data->path, filename) == 0;
+ }
+ }
+ return false;
+}
static int find_font_index(const char* path)
{
- int index = 0, handle;
-
- while (index < MAXFONTS)
+ for(int index = 0; index < MAXFONTS; index++)
{
- handle = buflib_allocations[index];
- if (handle > 0 && !strcmp(core_get_name(handle), path))
+ if(font_filename_matches_loaded_id(index, path))
+ {
+ logf("%s Found id: [%d], %s", __func__, index, path);
return index;
- index++;
+ }
}
+ logf("%s %s Not found using id: [%d], FONT_SYSFIXED",
+ __func__, path, FONT_SYSFIXED);
return FONT_SYSFIXED;
}
-const char* font_filename(int font_id)
-{
- if ( font_id < 0 || font_id >= MAXFONTS )
- return NULL;
- int handle = buflib_allocations[font_id];
- if (handle > 0)
- return core_get_name(handle);
- return NULL;
-}
-
static size_t font_glyphs_to_bufsize(struct font *pf, int glyphs)
{
size_t bufsize;
@@ -406,6 +410,11 @@ static struct font* font_load_header(int fd, struct font *pheader,
/* load a font with room for glyphs, limited to bufsize if not zero */
int font_load_ex( const char *path, size_t buf_size, int glyphs )
{
+ /* needed to handle the font properly after it's loaded */
+ size_t path_len = strlen(path);
+ if ( path_len >= MAX_PATH )
+ return -1;
+
//printf("\nfont_load_ex(%s, %d, %d)\n", path, buf_size, glyphs);
int fd = open(path, O_RDONLY|O_BINARY);
if ( fd < 0 )
@@ -457,7 +466,7 @@ int font_load_ex( const char *path, size_t buf_size, int glyphs )
{
/* already loaded, no need to reload */
struct buflib_alloc_data *pd = core_get_data(buflib_allocations[font_id]);
- if (pd->font.buffer_size < bufsize)
+ if (pd->font.buffer_size < bufsize || pd->path_bufsz < path_len)
{
int old_refcount, old_id;
size_t old_bufsize = pd->font.buffer_size;
@@ -512,19 +521,22 @@ int font_load_ex( const char *path, size_t buf_size, int glyphs )
if ( open_slot == -1 )
return -1;
font_id = open_slot;
-
+ size_t path_bufsz = MAX(path_len + 1, 64); /* enough size for common case */
/* allocate mem */
- int handle = core_alloc_ex( path,
- bufsize + sizeof( struct buflib_alloc_data ),
+ int handle = core_alloc_ex(
+ bufsize + path_bufsz + sizeof( struct buflib_alloc_data ),
&buflibops );
if ( handle <= 0 )
{
return -1;
}
struct buflib_alloc_data *pdata;
- pdata = core_get_data(handle);
- pdata->handle_locks = 1;
+
+ pdata = core_get_data_pinned(handle);
pdata->refcount = 1;
+ pdata->path = pdata->buffer + bufsize;
+ /* save load path so we can recognize this font later */
+ memcpy(pdata->path, path, path_len+1);
/* load and init */
struct font *pf = &pdata->font;
@@ -577,7 +589,8 @@ int font_load_ex( const char *path, size_t buf_size, int glyphs )
}
buflib_allocations[font_id] = handle;
//printf("%s -> [%d] -> %d\n", path, font_id, *handle);
- lock_font_handle( handle, false );
+ core_put_data_pinned(pdata);
+ logf("%s id: [%d], %s", __func__, font_id, path);
return font_id; /* success!*/
}
@@ -598,14 +611,13 @@ void font_unload(int font_id)
pdata->refcount--;
if (pdata->refcount < 1)
{
- //printf("freeing id: %d %s\n", font_id, core_get_name(*handle));
+ logf("%s %s", __func__, pdata->path);
if (pf && pf->fd >= 0)
{
glyph_cache_save(font_id);
close(pf->fd);
}
- if (handle > 0)
- core_free(handle);
+ core_free(handle);
buflib_allocations[font_id] = -1;
}
@@ -659,15 +671,15 @@ static void font_enable(int font_id)
int handle = buflib_allocations[font_id];
if ( handle < 0 )
return;
- struct buflib_alloc_data *pdata = core_get_data(handle);
+ struct buflib_alloc_data *pdata = core_get_data_pinned(handle);
struct font *pf = &pdata->font;
if (pf->disabled && pf->fd < 0)
{
- const char *filename = font_filename(font_id);
- pf->fd = open(filename, O_RDONLY);
+ pf->fd = open(pdata->path, O_RDONLY);
pf->disabled = false;
}
+ core_put_data_pinned(pdata);
}
void font_enable_all(void)
@@ -687,7 +699,7 @@ struct font* font_get(int font)
struct font* pf;
if (font == FONT_UI)
font = MAXFONTS-1;
- if (font <= FONT_SYSFIXED)
+ if (font <= FONT_SYSFIXED || font >= MAXFONTS)
return &sysfont;
while (1) {
@@ -859,9 +871,10 @@ const unsigned char* font_get_bits(struct font* pf, unsigned short char_code)
static void font_path_to_glyph_path( const char *font_path, char *glyph_path)
{
/* take full file name, cut extension, and add .glyphcache */
- strlcpy(glyph_path, font_path, MAX_PATH);
- glyph_path[strlen(glyph_path)-4] = '\0';
- strcat(glyph_path, ".gc");
+ strmemccpy(glyph_path, font_path, MAX_PATH);
+ int dotidx = strlen(glyph_path) - sizeof(FONT_EXT);
+ strmemccpy(glyph_path + dotidx, "." GLYPH_CACHE_EXT, MAX_PATH - dotidx);
+ logf("%s %s", __func__, glyph_path);
}
/* call with NULL to flush */
@@ -903,25 +916,28 @@ static void glyph_cache_save(int font_id)
if ( handle < 0 )
return;
- struct font *pf = pf_from_handle(handle);
+ struct buflib_alloc_data *pdata = core_get_data_pinned(handle);
+ struct font *pf = &pdata->font;
+
if(pf && pf->fd >= 0)
{
char filename[MAX_PATH];
- font_path_to_glyph_path(font_filename(font_id), filename);
+ font_path_to_glyph_path(pdata->path, filename);
fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
- if (fd < 0)
- return;
-
- cache_pf = pf;
- cache_fd = fd;
- lru_traverse(&cache_pf->cache._lru, glyph_file_write);
- glyph_file_write(NULL);
- if (cache_fd >= 0)
+ if (fd >= 0)
{
- close(cache_fd);
- cache_fd = -1;
+ cache_pf = pf;
+ cache_fd = fd;
+ lru_traverse(&cache_pf->cache._lru, glyph_file_write);
+ glyph_file_write(NULL);
+ if (cache_fd >= 0)
+ {
+ close(cache_fd);
+ cache_fd = -1;
+ }
}
}
+ core_put_data_pinned(pdata);
return;
}
@@ -1051,16 +1067,20 @@ const unsigned char* font_get_bits(struct font* pf, unsigned short char_code)
#endif /* BOOTLOADER */
/*
- * Returns the stringsize of a given string.
+ * Returns the stringsize of a given NULL terminated string
+ * stops after maxbytes or NULL (\0) whichever occurs first.
+ * maxbytes = -1 ignores maxbytes and relies on NULL terminator (\0)
+ * to terminate the string
*/
-int font_getstringsize(const unsigned char *str, int *w, int *h, int fontnumber)
+int font_getstringnsize(const unsigned char *str, size_t maxbytes, int *w, int *h, int fontnum)
{
- struct font* pf = font_get(fontnumber);
+ struct font* pf = font_get(fontnum);
+ font_lock( fontnum, true );
unsigned short ch;
int width = 0;
+ size_t b = maxbytes - 1;
- font_lock( fontnumber, true );
- for (str = utf8decode(str, &ch); ch != 0 ; str = utf8decode(str, &ch))
+ for (str = utf8decode(str, &ch); ch != 0 && b < maxbytes; str = utf8decode(str, &ch), b--)
{
if (is_diacritic(ch, NULL))
continue;
@@ -1072,10 +1092,18 @@ int font_getstringsize(const unsigned char *str, int *w, int *h, int fontnumber)
*w = width;
if ( h )
*h = pf->height;
- font_lock( fontnumber, false );
+ font_lock( fontnum, false );
return width;
}
+/*
+ * Returns the stringsize of a given NULL terminated string.
+ */
+int font_getstringsize(const unsigned char *str, int *w, int *h, int fontnumber)
+{
+ return font_getstringnsize(str, -1, w, h, fontnumber);
+}
+
/* -----------------------------------------------------------------
* vim: et sw=4 ts=8 sts=4 tw=78
*/
diff --git a/firmware/general.c b/firmware/general.c
index 8508b34b88..d421d722a8 100644
--- a/firmware/general.c
+++ b/firmware/general.c
@@ -107,7 +107,7 @@ char *create_numbered_filename(char *buffer, const char *path,
int suffixlen = strlen(suffix);
if (buffer != path)
- strlcpy(buffer, path, MAX_PATH);
+ strmemccpy(buffer, path, MAX_PATH);
pathlen = strlen(buffer);
@@ -181,7 +181,7 @@ char *create_datetime_filename(char *buffer, const char *path,
last_tm = *tm;
if (buffer != path)
- strlcpy(buffer, path, MAX_PATH);
+ strmemccpy(buffer, path, MAX_PATH);
pathlen = strlen(buffer);
snprintf(buffer + pathlen, MAX_PATH - pathlen,
diff --git a/firmware/ifp_usb_serial.c b/firmware/ifp_usb_serial.c
deleted file mode 100644
index f7e3a51858..0000000000
--- a/firmware/ifp_usb_serial.c
+++ /dev/null
@@ -1,1121 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2006 by Tomasz Malesinski
- *
- * 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.
- *
- ****************************************************************************/
-
-/*
-#define LCD_DEBUG
-#define BUTTONS
-*/
-
-/* #include "config.h" */
-#include <stdlib.h>
-#include "pnx0101.h"
-#include "ifp_usb_serial.h"
-
-#ifdef BUTTONS
-#include "kernel.h"
-#include "button.h"
-#include "system.h"
-#endif
-
-#ifdef LCD_DEBUG
-#include "lcd.h"
-#endif
-
-
-#define ISP1582_BASE (0x24100000)
-#define ISP1582_ADDRESS (*(volatile unsigned char *)ISP1582_BASE)
-#define ISP1582_MODE (*(volatile unsigned short *)(ISP1582_BASE + 0xc))
-#define ISP1582_INTCONF (*(volatile unsigned char *)(ISP1582_BASE + 0x10))
-#define ISP1582_OTG (*(volatile unsigned char *)(ISP1582_BASE + 0x12))
-#define ISP1582_INTEN (*(volatile unsigned long *)(ISP1582_BASE + 0x14))
-
-#define ISP1582_EPINDEX (*(volatile unsigned char *)(ISP1582_BASE + 0x2c))
-#define ISP1582_CTRLFUN (*(volatile unsigned char *)(ISP1582_BASE + 0x28))
-#define ISP1582_DATA (*(volatile unsigned short *)(ISP1582_BASE + 0x20))
-#define ISP1582_BUFLEN (*(volatile unsigned short *)(ISP1582_BASE + 0x1c))
-#define ISP1582_BUFSTAT (*(volatile unsigned char *)(ISP1582_BASE + 0x1e))
-#define ISP1582_MAXPKSZ (*(volatile unsigned short *)(ISP1582_BASE + 0x04))
-#define ISP1582_EPTYPE (*(volatile unsigned short *)(ISP1582_BASE + 0x08))
-
-#define ISP1582_INT (*(volatile unsigned long *)(ISP1582_BASE + 0x18))
-#define ISP1582_CHIPID (*(volatile unsigned long *)(ISP1582_BASE + 0x70))
-#define ISP1582_FRAMENO (*(volatile unsigned short *)(ISP1582_BASE + 0x74))
-#define ISP1582_UNLOCK (*(volatile unsigned short *)(ISP1582_BASE + 0x7c))
-
-#define ISP1582_UNLOCK_CODE 0xaa37
-
-#define TYPE_BULK 2
-
-#define STATE_DEFAULT 0
-#define STATE_ADDRESS 1
-#define STATE_CONFIGURED 2
-
-#define N_ENDPOINTS 2
-
-struct usb_endpoint
-{
- unsigned char *out_buf;
- short out_len;
- short out_ptr;
- void (*out_done)(int, unsigned char *, int);
- unsigned char out_in_progress;
-
- unsigned char *in_buf;
- short in_min_len;
- short in_max_len;
- short in_ptr;
- void (*in_done)(int, unsigned char *, int);
- unsigned char in_ack;
-
- unsigned char halt[2];
- unsigned char enabled[2];
- short max_pkt_size[2];
-};
-
-static char usb_connect_state;
-
-static struct usb_endpoint endpoints[N_ENDPOINTS];
-
-static unsigned char setup_pkt_buf[8];
-static unsigned char setup_out_buf[8];
-static unsigned char usb_state;
-static unsigned char usb_remote_wakeup;
-
-#ifdef LCD_DEBUG
-static unsigned char int_count[32];
-
-static int log_pos_x = 0;
-static int log_pos_y = 3;
-#endif
-
-static void nop_f(void)
-{
-}
-
-#ifdef LCD_DEBUG
-static void log_char(char c)
-{
- char s[2];
-
- s[0] = c;
- s[1] = 0;
-
- lcd_puts(log_pos_x, log_pos_y, s);
- lcd_update();
- log_pos_x++;
- if (log_pos_x >= 16)
- {
- log_pos_x = 0;
- log_pos_y++;
- if (log_pos_y > 5)
- log_pos_y = 3;
- }
-}
-#else
-#define log_char(c)
-#endif
-
-#define SERIAL_BUF_SIZE 1024
-
-struct serial_fifo
-{
- unsigned char buf[SERIAL_BUF_SIZE];
- short head, tail;
-};
-
-static struct serial_fifo serial_in_fifo;
-static struct serial_fifo serial_out_fifo;
-static unsigned char serial_in_pkt[64];
-
-static unsigned char device_descriptor[18] = {
- 0x12, /* length */
- 0x01, /* descriptor type */
- 0x10, 0x01, /* USB version (1.1) */
- 0xff, 0xff, /* class and subclass */
- 0xff, /* protocol */
- 0x40, /* max packet size 0 */
- 0x02, 0x41, /* vendor (iRiver) */
- 0x07, 0xee, /* product (0xee07) */
- 0x01, 0x00, /* device version */
- 0x01, /* manufacturer string */
- 0x02, /* product string */
- 0x00, /* serial number string */
- 0x01 /* number of configurations */
-};
-
-static unsigned char cfg_descriptor[32] = {
- 0x09, /* length */
- 0x02, /* descriptor type */
- 0x20, 0x00, /* total length */
- 0x01, /* number of interfaces */
- 0x01, /* configuration value */
- 0x00, /* configuration string */
- 0x80, /* attributes (none) */
- 0x32, /* max power (100 mA) */
- /* interface descriptor */
- 0x09, /* length */
- 0x04, /* descriptor type */
- 0x00, /* interface number */
- 0x00, /* alternate setting */
- 0x02, /* number of endpoints */
- 0xff, /* interface class */
- 0xff, /* interface subclass */
- 0xff, /* interface protocol */
- 0x00, /* interface string */
- /* endpoint IN */
- 0x07, /* length */
- 0x05, /* descriptor type */
- 0x81, /* endpoint 1 IN */
- 0x02, /* attributes (bulk) */
- 0x40, 0x00, /* max packet size */
- 0x00, /* interval */
- /* endpoint OUT */
- 0x07, /* length */
- 0x05, /* descriptor type */
- 0x01, /* endpoint 1 IN */
- 0x02, /* attributes (bulk) */
- 0x40, 0x00, /* max packet size */
- 0x00 /* interval */
-};
-
-static unsigned char lang_descriptor[4] = {
- 0x04, /* length */
- 0x03, /* descriptor type */
- 0x09, 0x04 /* English (US) */
-};
-
-#define N_STRING_DESCRIPTORS 2
-
-static unsigned char string_descriptor_vendor[] = {
- 0x2e, 0x03,
- 'i', 0, 'R', 0, 'i', 0, 'v', 0, 'e', 0, 'r', 0, ' ', 0, 'L', 0,
- 't', 0, 'd', 0, ' ', 0, 'a', 0, 'n', 0, 'd', 0, ' ', 0, 'R', 0,
- 'o', 0, 'c', 0, 'k', 0, 'b', 0, 'o', 0, 'x', 0};
-
-static unsigned char string_descriptor_product[] = {
- 0x1c, 0x03,
- 'i', 0, 'R', 0, 'i', 0, 'v', 0, 'e', 0, 'r', 0, ' ', 0, 'i', 0,
- 'F', 0, 'P', 0, '7', 0, '0', 0, '0', 0};
-
-static unsigned char *string_descriptor[N_STRING_DESCRIPTORS] = {
- string_descriptor_vendor,
- string_descriptor_product
-};
-
-static inline int ep_index(int n, int dir)
-{
- return (n << 1) | dir;
-}
-
-static inline int epidx_dir(int idx)
-{
- return idx & 1;
-}
-
-static inline int epidx_n(int idx)
-{
- return idx >> 1;
-}
-
-int usb_connected(void)
-{
- return GPIO7_READ & 1;
-}
-
-static inline void usb_select_endpoint(int idx)
-{
- ISP1582_EPINDEX = idx;
-}
-
-static inline void usb_select_setup_endpoint(void)
-{
- ISP1582_EPINDEX = 0x20;
-}
-
-static void usb_setup_endpoint(int idx, int max_pkt_size, int type)
-{
- struct usb_endpoint *ep;
-
- usb_select_endpoint(idx);
- ISP1582_MAXPKSZ = max_pkt_size;
- /* |= is in the original firmware */
- ISP1582_EPTYPE |= 0x1c | type;
- /* clear buffer */
- ISP1582_CTRLFUN |= 0x10;
- ISP1582_INTEN |= (1 << (10 + idx));
-
- ep = &(endpoints[epidx_n(idx)]);
- ep->halt[epidx_dir(idx)] = 0;
- ep->enabled[epidx_dir(idx)] = 1;
- ep->out_in_progress = 0;
- ep->in_min_len = -1;
- ep->in_ack = 0;
- ep->max_pkt_size[epidx_dir(idx)] = max_pkt_size;
-}
-
-static void usb_disable_endpoint(int idx)
-{
- usb_select_endpoint(idx);
- ISP1582_EPTYPE &= 8;
- ISP1582_INTEN &= ~(1 << (10 + idx));
- endpoints[epidx_n(idx)].enabled[epidx_dir(idx)] = 1;
-}
-
-void usb_reconnect(void)
-{
- int i;
- ISP1582_MODE &= ~1; /* SOFTCT off */
- for (i = 0; i < 10000; i++)
- nop_f();
- ISP1582_MODE |= 1; /* SOFTCT on */
-}
-
-static void usb_cleanup(void)
-{
- ISP1582_MODE &= ~1; /* SOFTCT off */
-}
-
-static void usb_setup(int reset)
-{
- int i;
-
- for (i = 0; i < N_ENDPOINTS; i++)
- endpoints[i].enabled[0] = endpoints[i].enabled[1] = 0;
-
- ISP1582_UNLOCK = ISP1582_UNLOCK_CODE;
- if (!reset)
- ISP1582_MODE = 0x88; /* CLKAON | GLINTENA */
- ISP1582_INTCONF = 0x57;
- ISP1582_INTEN = 0xd39;
-
- ISP1582_ADDRESS = reset ? 0x80: 0;
-
- usb_setup_endpoint(ep_index(0, DIR_RX), 64, 0);
- usb_setup_endpoint(ep_index(0, DIR_TX), 64, 0);
-
- ISP1582_MODE |= 1; /* SOFTCT on */
-
- usb_state = STATE_DEFAULT;
- usb_remote_wakeup = 0;
-}
-
-static int usb_get_packet(unsigned char *buf, int max_len)
-{
- int len, i;
- len = ISP1582_BUFLEN;
-
- if (max_len < 0 || max_len > len)
- max_len = len;
-
- i = 0;
- while (i < len)
- {
- unsigned short d = ISP1582_DATA;
- if (i < max_len)
- buf[i] = d & 0xff;
- i++;
- if (i < max_len)
- buf[i] = (d >> 8) & 0xff;
- i++;
- }
- return max_len;
-}
-
-static void usb_receive(int n)
-{
- int len;
-
- if (endpoints[n].halt[DIR_RX]
- || !endpoints[n].enabled[DIR_RX]
- || endpoints[n].in_min_len < 0
- || !endpoints[n].in_ack)
- return;
-
- endpoints[n].in_ack = 0;
-
- usb_select_endpoint(ep_index(n, DIR_RX));
-
- len = usb_get_packet(endpoints[n].in_buf + endpoints[n].in_ptr,
- endpoints[n].in_max_len - endpoints[n].in_ptr);
- endpoints[n].in_ptr += len;
- if (endpoints[n].in_ptr >= endpoints[n].in_min_len) {
- endpoints[n].in_min_len = -1;
- if (endpoints[n].in_done)
- (*(endpoints[n].in_done))(n, endpoints[n].in_buf,
- endpoints[n].in_ptr);
- }
-}
-
-static int usb_out_buffer_full(int ep)
-{
- usb_select_endpoint(ep_index(ep, DIR_TX));
- if (ISP1582_EPTYPE & 4)
- return (ISP1582_BUFSTAT & 3) == 3;
- else
- return (ISP1582_BUFSTAT & 3) != 0;
-}
-
-static void usb_send(int n)
-{
- int max_pkt_size, len;
- int i;
- unsigned char *p;
-
-#ifdef LCD_DEBUG
- if (endpoints[n].halt[DIR_TX])
- log_char('H');
- if (!endpoints[n].out_in_progress)
- log_char('$');
-#endif
-
- if (endpoints[n].halt[DIR_TX]
- || !endpoints[n].enabled[DIR_TX]
- || !endpoints[n].out_in_progress)
- return;
-
- if (endpoints[n].out_ptr < 0)
- {
- endpoints[n].out_in_progress = 0;
- if (endpoints[n].out_done)
- (*(endpoints[n].out_done))(n, endpoints[n].out_buf,
- endpoints[n].out_len);
- return;
- }
-
- if (usb_out_buffer_full(n))
- {
- log_char('F');
- return;
- }
-
- usb_select_endpoint(ep_index(n, DIR_TX));
- max_pkt_size = endpoints[n].max_pkt_size[DIR_TX];
- len = endpoints[n].out_len - endpoints[n].out_ptr;
- if (len > max_pkt_size)
- len = max_pkt_size;
-
- log_char('0' + (len % 10));
- ISP1582_BUFLEN = len;
- p = endpoints[n].out_buf + endpoints[n].out_ptr;
- i = 0;
- while (len - i >= 2) {
- ISP1582_DATA = p[i] | (p[i + 1] << 8);
- i += 2;
- }
- if (i < len)
- ISP1582_DATA = p[i];
-
- endpoints[n].out_ptr += len;
-
-/*
- if (endpoints[n].out_ptr == endpoints[n].out_len
- && len < max_pkt_size)
-*/
- if (endpoints[n].out_ptr == endpoints[n].out_len)
- endpoints[n].out_ptr = -1;
-}
-
-static void usb_stall_endpoint(int idx)
-{
- usb_select_endpoint(idx);
- ISP1582_CTRLFUN |= 1;
- endpoints[epidx_n(idx)].halt[epidx_dir(idx)] = 1;
-}
-
-static void usb_unstall_endpoint(int idx)
-{
- usb_select_endpoint(idx);
- ISP1582_CTRLFUN &= ~1;
- ISP1582_EPTYPE &= ~8;
- ISP1582_EPTYPE |= 8;
- ISP1582_CTRLFUN |= 0x10;
- if (epidx_dir(idx) == DIR_TX)
- endpoints[epidx_n(idx)].out_in_progress = 0;
- else
- {
- endpoints[epidx_n(idx)].in_min_len = -1;
- endpoints[epidx_n(idx)].in_ack = 0;
- }
- endpoints[epidx_n(idx)].halt[epidx_dir(idx)] = 0;
-}
-
-static void usb_status_ack(int dir)
-{
- log_char(dir ? '@' : '#');
- usb_select_endpoint(ep_index(0, dir));
- ISP1582_CTRLFUN |= 2;
-}
-
-static void usb_set_address(int adr)
-{
- ISP1582_ADDRESS = adr | 0x80;
-}
-
-static void usb_data_stage_enable(int dir)
-{
- usb_select_endpoint(ep_index(0, dir));
- ISP1582_CTRLFUN |= 4;
-}
-
-static void usb_request_error(void)
-{
- usb_stall_endpoint(ep_index(0, DIR_TX));
- usb_stall_endpoint(ep_index(0, DIR_RX));
-}
-
-static void usb_receive_block(unsigned char *buf, int min_len,
- int max_len,
- void (*in_done)(int, unsigned char *, int),
- int ep)
-{
- endpoints[ep].in_done = in_done;
- endpoints[ep].in_buf = buf;
- endpoints[ep].in_max_len = max_len;
- endpoints[ep].in_min_len = min_len;
- endpoints[ep].in_ptr = 0;
- usb_receive(ep);
-}
-
-static void usb_send_block(unsigned char *buf, int len,
- void (*done)(int, unsigned char *, int),
- int ep)
-{
- endpoints[ep].out_done = done;
- endpoints[ep].out_buf = buf;
- endpoints[ep].out_len = len;
- endpoints[ep].out_ptr = 0;
- endpoints[ep].out_in_progress = 1;
- usb_send(ep);
-}
-
-static void out_send_status(int n, unsigned char *buf, int len)
-{
- (void)n;
- (void)buf;
- (void)len;
- usb_status_ack(DIR_RX);
-}
-
-static void usb_send_block_and_status(unsigned char *buf, int len, int ep)
-{
- usb_send_block(buf, len, out_send_status, ep);
-}
-
-static void usb_setup_set_address(int adr)
-{
- usb_set_address(adr);
- usb_state = adr ? STATE_ADDRESS : STATE_DEFAULT;
- usb_status_ack(DIR_TX);
-}
-
-static void usb_send_descriptor(unsigned char *device_descriptor,
- int descriptor_len, int buffer_len)
-{
- int len = descriptor_len < buffer_len ? descriptor_len : buffer_len;
- usb_send_block_and_status(device_descriptor, len, 0);
-}
-
-static void usb_setup_get_descriptor(int type, int index, int lang, int len)
-{
- (void)lang;
- usb_data_stage_enable(DIR_TX);
- switch (type)
- {
- case 1:
- if (index == 0)
- usb_send_descriptor(device_descriptor,
- sizeof(device_descriptor), len);
- else
- usb_request_error();
- break;
- case 2:
- if (index == 0)
- usb_send_descriptor(cfg_descriptor,
- sizeof(cfg_descriptor), len);
- else
- usb_request_error();
- break;
- case 3:
- if (index == 0)
- usb_send_descriptor(lang_descriptor,
- sizeof(lang_descriptor), len);
- else if (index <= N_STRING_DESCRIPTORS)
- usb_send_descriptor(string_descriptor[index - 1],
- string_descriptor[index - 1][0],
- len);
- else
- usb_request_error();
- break;
- default:
- usb_request_error();
- }
-}
-
-static void usb_setup_get_configuration(void)
-{
- setup_out_buf[0] = (usb_state == STATE_CONFIGURED) ? 1 : 0;
- usb_data_stage_enable(DIR_TX);
- usb_send_block_and_status(setup_out_buf, 1, 0);
-}
-
-static void usb_setup_interface(void)
-{
- usb_setup_endpoint(ep_index(1, DIR_RX), 64, TYPE_BULK);
- usb_setup_endpoint(ep_index(1, DIR_TX), 64, TYPE_BULK);
-}
-
-static void usb_setup_set_configuration(int value)
-{
- switch (value)
- {
- case 0:
- usb_disable_endpoint(ep_index(1, DIR_RX));
- usb_disable_endpoint(ep_index(1, DIR_TX));
- usb_state = STATE_ADDRESS;
- usb_status_ack(DIR_TX);
- break;
- case 1:
- usb_setup_interface();
- usb_state = STATE_CONFIGURED;
- usb_status_ack(DIR_TX);
- break;
- default:
- usb_request_error();
- }
-}
-
-static void usb_send_status(void)
-{
- usb_data_stage_enable(DIR_TX);
- usb_send_block_and_status(setup_out_buf, 2, 0);
-}
-
-static void usb_setup_get_device_status(void)
-{
- setup_out_buf[0] = (usb_remote_wakeup != 0) ? 2 : 0;
- setup_out_buf[1] = 0;
- usb_send_status();
-}
-
-static void usb_setup_clear_device_feature(int feature)
-{
- if (feature == 1) {
- usb_remote_wakeup = 0;
- usb_status_ack(DIR_TX);
- } else
- usb_request_error();
-}
-
-static void usb_setup_set_device_feature(int feature)
-{
- if (feature == 1) {
- usb_remote_wakeup = 1;
- usb_status_ack(DIR_TX);
- } else
- usb_request_error();
-}
-
-static void usb_setup_clear_endpoint_feature(int endpoint, int feature)
-{
- if (usb_state != STATE_CONFIGURED || feature != 0)
- usb_request_error();
- else if ((endpoint & 0xf) == 1)
- {
- usb_unstall_endpoint(ep_index(endpoint & 0xf, endpoint >> 7));
- usb_status_ack(DIR_TX);
- }
- else
- usb_request_error();
-}
-
-static void usb_setup_set_endpoint_feature(int endpoint, int feature)
-{
- if (usb_state != STATE_CONFIGURED || feature != 0)
- usb_request_error();
- else if ((endpoint & 0xf) == 1)
- {
- usb_stall_endpoint(ep_index(endpoint & 0xf, endpoint >> 7));
- usb_status_ack(DIR_TX);
- }
- else
- usb_request_error();
-}
-
-static void usb_setup_get_interface_status(int interface)
-{
- if (usb_state != STATE_CONFIGURED || interface != 0)
- usb_request_error();
- else
- {
- setup_out_buf[0] = setup_out_buf[1] = 0;
- usb_send_status();
- }
-}
-
-static void usb_setup_get_endpoint_status(int endpoint)
-{
- if ((usb_state == STATE_CONFIGURED && (endpoint & 0xf) <= 1)
- || (usb_state == STATE_ADDRESS && (endpoint & 0xf) == 0))
- {
- setup_out_buf[0] = endpoints[endpoint & 0xf].halt[endpoint >> 7];
- setup_out_buf[1] = 0;
- usb_send_status();
- }
- else
- usb_request_error();
-}
-
-static void usb_setup_get_interface(int interface)
-{
- if (usb_state != STATE_CONFIGURED || interface != 0)
- usb_request_error();
- else
- {
- setup_out_buf[0] = 0;
- usb_data_stage_enable(DIR_TX);
- usb_send_block_and_status(setup_out_buf, 1, 0);
- }
-}
-
-static void usb_setup_set_interface(int interface, int setting)
-{
- if (usb_state != STATE_CONFIGURED || interface != 0 || setting != 0)
- usb_request_error();
- else
- {
- usb_setup_interface();
- usb_status_ack(DIR_TX);
- }
-}
-
-static void usb_handle_setup_pkt(unsigned char *pkt)
-{
- switch ((pkt[0] << 8) | pkt[1])
- {
- case 0x0005:
- log_char('A');
- usb_setup_set_address(pkt[2]);
- break;
- case 0x8006:
- log_char('D');
- usb_setup_get_descriptor(pkt[3], pkt[2], (pkt[5] << 8) | pkt[4],
- (pkt[7] << 8) | pkt[6]);
- break;
- case 0x8008:
- usb_setup_get_configuration();
- break;
- case 0x0009:
- usb_setup_set_configuration(pkt[2]);
- break;
- case 0x8000:
- usb_setup_get_device_status();
- break;
- case 0x8100:
- usb_setup_get_interface_status(pkt[4]);
- break;
- case 0x8200:
- usb_setup_get_endpoint_status(pkt[4]);
- break;
- case 0x0001:
- usb_setup_clear_device_feature(pkt[2]);
- break;
- case 0x0201:
- usb_setup_clear_endpoint_feature(pkt[4], pkt[2]);
- break;
- case 0x0003:
- usb_setup_set_device_feature(pkt[2]);
- break;
- case 0x0203:
- usb_setup_set_endpoint_feature(pkt[4], pkt[2]);
- break;
- case 0x810a:
- usb_setup_get_interface(pkt[4]);
- break;
- case 0x010b:
- usb_setup_set_interface(pkt[4], pkt[2]);
- break;
- case 0x0103:
- /* set interface feature */
- case 0x0101:
- /* clear interface feature */
- case 0x0007:
- /* set descriptor */
- case 0x820c:
- /* synch frame */
- default:
- usb_request_error();
- }
-}
-
-static void usb_handle_setup_rx(void)
-{
- int len;
-#ifdef LCD_DEBUG
- char s[20];
- int i;
-#endif
- usb_select_setup_endpoint();
- len = usb_get_packet(setup_pkt_buf, 8);
-
- if (len == 8)
- usb_handle_setup_pkt(setup_pkt_buf);
-
-#ifdef LCD_DEBUG
-/*
- snprintf(s, 10, "l%02x", len);
- lcd_puts(0, 5, s);
-*/
- for (i = 0; i < 8; i++)
- snprintf(s + i * 2, 3, "%02x", setup_pkt_buf[i]);
- lcd_puts(0, 0, s);
- lcd_update();
-#endif
-}
-
-static void usb_handle_data_int(int ep, int dir)
-{
- if (dir == DIR_TX)
- usb_send(ep);
- else
- {
- endpoints[ep].in_ack = 1;
- usb_receive(ep);
- }
-}
-
-static void usb_handle_int(int i)
-{
-#ifdef LCD_DEBUG
-/*
- char s[10];
- snprintf(s, sizeof(s), "%02d", i);
- lcd_puts(0, 2, s);
- lcd_update();
-*/
- int_count[i]++;
- if (i == 10)
- log_char('o');
- if (i == 11)
- log_char('i');
- if (i == 12)
- log_char('O');
- if (i == 13)
- log_char('I');
-#endif
-
- if (i >= 10)
- usb_handle_data_int((i - 10) / 2, i % 2);
- else
- {
- switch (i)
- {
- case 0:
- log_char('r');
- usb_setup(1);
- break;
- case 8:
- log_char('s');
- usb_handle_setup_rx();
- break;
- }
- }
-
-}
-
-static void usb_handle_interrupts(void)
-{
-#ifdef LCD_DEBUG
- char s[20];
-#endif
-
- while (1)
- {
- unsigned long ints;
- int i;
-
-#ifdef LCD_DEBUG
- /*
- snprintf(s, sizeof(s), "i%08lx", ISP1582_INT);
- lcd_puts(0, 2, s);
- */
-#endif
-
- ints = ISP1582_INT & ISP1582_INTEN;
- if (!ints) break;
-
- i = 0;
- while (!(ints & (1 << i)))
- i++;
- ISP1582_INT = 1 << i;
- usb_handle_int(i);
-
-#ifdef LCD_DEBUG
- for (i = 0; i < 8; i++)
- snprintf(s + i * 2, 3, "%02x", int_count[i]);
- lcd_puts(0, 6, s);
- for (i = 0; i < 8; i++)
- snprintf(s + i * 2, 3, "%02x", int_count[i + 8]);
- lcd_puts(0, 7, s);
-#endif
- }
-#ifdef LCD_DEBUG
-/*
- lcd_puts(0, 3, usb_connected() ? "C" : "N");
- lcd_update();
-*/
-#endif
-}
-
-static inline int fifo_mod(int n)
-{
- return (n >= SERIAL_BUF_SIZE) ? n - SERIAL_BUF_SIZE : n;
-}
-
-static void fifo_init(struct serial_fifo *fifo)
-{
- fifo->head = fifo->tail = 0;
-}
-
-static int fifo_empty(struct serial_fifo *fifo)
-{
- return fifo->head == fifo->tail;
-}
-
-static int fifo_full(struct serial_fifo *fifo)
-{
- return fifo_mod(fifo->head + 1) == fifo->tail;
-}
-
-static void fifo_remove(struct serial_fifo *fifo, int n)
-{
- fifo->tail = fifo_mod(fifo->tail + n);
-}
-
-/*
- Not used:
-static void fifo_add(struct serial_fifo *fifo, int n)
-{
- fifo->head = fifo_mod(fifo->head + n);
-}
-
-static void fifo_free_block(struct serial_fifo *fifo,
- unsigned char **ptr, int *len)
-{
- *ptr = fifo->buf + fifo->head;
- if (fifo->head >= fifo->tail)
- {
- int l = SERIAL_BUF_SIZE - fifo->head;
- if (fifo->tail == 0)
- l--;
- *len = l;
- }
- else
- *len = fifo->tail - fifo->head - 1;
-}
-*/
-
-static int fifo_free_space(struct serial_fifo *fifo)
-{
- if (fifo->head >= fifo->tail)
- return SERIAL_BUF_SIZE - (fifo->head - fifo->tail) - 1;
- else
- return fifo->tail - fifo->head - 1;
-}
-
-static int fifo_get_byte(struct serial_fifo *fifo)
-{
- int r = fifo->buf[fifo->tail];
- fifo->tail = fifo_mod(fifo->tail + 1);
- return r;
-}
-
-static void fifo_put_byte(struct serial_fifo *fifo, int b)
-{
- fifo->buf[fifo->head] = b;
- fifo->head = fifo_mod(fifo->head + 1);
-}
-
-static void fifo_full_block(struct serial_fifo *fifo,
- unsigned char **ptr, int *len)
-{
- *ptr = fifo->buf + fifo->tail;
- if (fifo->head >= fifo->tail)
- *len = fifo->head - fifo->tail;
- else
- *len = SERIAL_BUF_SIZE - fifo->tail;
-}
-
-static void serial_fill_in_fifo(int ep, unsigned char *buf, int len);
-static void serial_free_out_fifo(int ep, unsigned char *buf, int len);
-
-static void serial_restart_input(int ep)
-{
- if (fifo_free_space(&serial_in_fifo) >= 64)
- usb_receive_block(serial_in_pkt, 1, 64, serial_fill_in_fifo, ep);
-}
-
-static void serial_fill_in_fifo(int ep, unsigned char *buf, int len)
-{
- int i;
- for (i = 0; i < len; i++)
- fifo_put_byte(&serial_in_fifo, buf[i]);
- serial_restart_input(ep);
-}
-
-static void serial_restart_output(int ep)
-{
- unsigned char *block;
- int blen;
- fifo_full_block(&serial_out_fifo, &block, &blen);
- if (blen)
- {
-#ifdef LCD_DEBUG
- lcd_putsf(0, 2, "o%03lx/%03x", block - serial_out_fifo.buf, blen);
- lcd_update();
-#endif
- usb_send_block(block, blen, serial_free_out_fifo, ep);
- }
-}
-
-static void serial_free_out_fifo(int ep, unsigned char *buf, int len)
-{
- (void)buf;
- fifo_remove(&serial_out_fifo, len);
- serial_restart_output(ep);
-}
-
-static void usb_serial_handle(void)
-{
-#ifdef BUTTONS
- static int t = 0;
-
- t++;
- if (t >= 1000)
- {
- int b;
- t = 0;
- yield();
- b = button_get(false);
- if (b == BUTTON_PLAY)
- system_reboot();
- else if (b & BUTTON_REL)
- usb_reconnect();
- }
-#endif
-
-
- if (!usb_connect_state)
- {
- if (usb_connected())
- {
- int i;
- GPIO3_SET = 4;
- (*(volatile unsigned long *)0x80005004) = 2;
- (*(volatile unsigned long *)0x80005008) = 0;
- for (i = 0; i < 100000; i++)
- nop_f();
- usb_setup(0);
- usb_connect_state = 1;
- }
- }
- else
- {
- if (!usb_connected())
- {
- usb_connect_state = 0;
- usb_cleanup();
- }
- else
- {
- usb_handle_interrupts();
-
- if (usb_state == STATE_CONFIGURED)
- {
- if (endpoints[1].in_min_len < 0)
- serial_restart_input(1);
- if (!endpoints[1].out_in_progress)
- serial_restart_output(1);
- }
- }
- }
-}
-
-
-/*
- Not used:
-static int usb_serial_in_empty(void)
-{
- return fifo_empty(&serial_in_fifo);
-}
-*/
-
-int usb_serial_get_byte(void)
-{
- while (fifo_empty(&serial_in_fifo))
- usb_serial_handle();
- return fifo_get_byte(&serial_in_fifo);
-}
-
-int usb_serial_try_get_byte(void)
-{
- int r;
- if (fifo_empty(&serial_in_fifo))
- r = -1;
- else
- r = fifo_get_byte(&serial_in_fifo);
- usb_serial_handle();
- return r;
-}
-
-/*
- Not used:
-static int usb_serial_out_full(void)
-{
- return fifo_full(&serial_out_fifo);
-}
-*/
-
-void usb_serial_put_byte(int b)
-{
- while (fifo_full(&serial_out_fifo))
- usb_serial_handle();
- fifo_put_byte(&serial_out_fifo, b);
- usb_serial_handle();
-}
-
-int usb_serial_try_put_byte(int b)
-{
- int r = -1;
- if (!fifo_full(&serial_out_fifo))
- {
- fifo_put_byte(&serial_out_fifo, b);
- r = 0;
- }
- usb_serial_handle();
- return r;
-}
-
-void usb_serial_init(void)
-{
- fifo_init(&serial_in_fifo);
- fifo_init(&serial_out_fifo);
- usb_connect_state = 0;
-}
diff --git a/firmware/target/arm/tcc77x/iaudio7/adc-target.h b/firmware/include/adler32.h
index 1916d93598..cd5302e869 100644
--- a/firmware/target/arm/tcc77x/iaudio7/adc-target.h
+++ b/firmware/include/adler32.h
@@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Copyright (C) 2007 Dave Chapman
+ * Copyright (C) 2021 James Buren (adaptations from tinf/zlib)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -18,11 +18,12 @@
* KIND, either express or implied.
*
****************************************************************************/
-#ifndef _ADC_TARGET_H_
-#define _ADC_TARGET_H_
-#define NUM_ADC_CHANNELS 8
+#include <stdint.h>
-#define ADC_BUTTONS 0
+#ifndef _ADLER32_H
+#define _ADLER32_H
-#endif /* _ADC_TARGET_H_ */
+uint32_t adler_32(const void *src, uint32_t len, uint32_t adler32);
+
+#endif
diff --git a/firmware/include/buflib.h b/firmware/include/buflib.h
index 7f534c6ce0..3fe8ac1430 100644
--- a/firmware/include/buflib.h
+++ b/firmware/include/buflib.h
@@ -1,382 +1,417 @@
-/***************************************************************************
-* __________ __ ___.
-* Open \______ \ ____ ____ | | _\_ |__ _______ ___
-* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
-* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
-* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
-* \/ \/ \/ \/ \/
-* $Id$
-*
-* This is a memory allocator designed to provide reasonable management of free
-* space and fast access to allocated data. More than one allocator can be used
-* at a time by initializing multiple contexts.
-*
-* Copyright (C) 2009 Andrew Mahone
-* Copyright (C) 2011 Thomas Martitz
-*
-* 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.
-*
-****************************************************************************/
-
+/**************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2009 Andrew Mahone
+ * Copyright (C) 2011 Thomas Martitz
+ * Copyright (C) 2023 Aidan MacDonald
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
#ifndef _BUFLIB_H_
#define _BUFLIB_H_
+
+#include "config.h"
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
-/* enable single block debugging */
-#define BUFLIB_DEBUG_BLOCK_SINGLE
+/* Add extra checks to buflib_get_data to catch bad handles */
+//#define BUFLIB_DEBUG_GET_DATA
-union buflib_data
-{
- intptr_t val; /* length of the block in n*sizeof(union buflib_data).
- Includes buflib metadata overhead. A negative value
- indicates block is unallocated */
- char name[1]; /* name, actually a variable sized string */
- struct buflib_callbacks* ops; /* callback functions for move and shrink. Can be NULL */
- char* alloc; /* start of allocated memory area */
- union buflib_data *handle; /* pointer to entry in the handle table.
- Used during compaction for fast lookup */
- uint32_t crc; /* checksum of this data to detect corruption */
-};
+/* Support integrity check */
+//#define BUFLIB_DEBUG_CHECK_VALID
-struct buflib_context
-{
- union buflib_data *handle_table;
- union buflib_data *first_free_handle;
- union buflib_data *last_handle;
- union buflib_data *buf_start;
- union buflib_data *alloc_end;
- bool compact;
-};
+/* Support debug printing of memory blocks */
+//#define BUFLIB_DEBUG_PRINT
-/**
- * This declares the minimal overhead that is required per alloc. These
- * are bytes that are allocated from the context's pool in addition
- * to the actually requested number of bytes.
- *
- * The total number of bytes consumed by an allocation is
- * BUFLIB_ALLOC_OVERHEAD + requested bytes + strlen(<name passed to
- * buflib_alloc_ex()) + pad to pointer size
- */
-#define BUFLIB_ALLOC_OVERHEAD (6*sizeof(union buflib_data))
+/* Defined by the backend header. */
+struct buflib_context;
+
+/* Buflib callback return codes. */
+#define BUFLIB_CB_OK 0
+#define BUFLIB_CB_CANNOT_MOVE 1
+#define BUFLIB_CB_CANNOT_SHRINK 1
+
+/* Buflib shrink hints. */
+#define BUFLIB_SHRINK_SIZE_MASK (~BUFLIB_SHRINK_POS_MASK)
+#define BUFLIB_SHRINK_POS_FRONT (1u<<31)
+#define BUFLIB_SHRINK_POS_BACK (1u<<30)
+#define BUFLIB_SHRINK_POS_MASK (BUFLIB_SHRINK_POS_FRONT|BUFLIB_SHRINK_POS_BACK)
/**
- * Callbacks used by the buflib to inform allocation that compaction
- * is happening (before data is moved)
- *
- * Note that buflib tries to move to satisfy new allocations before shrinking.
- * So if you have something to resize try to do it outside of the callback.
- *
- * Regardless of the above, if the allocation is SHRINKABLE, but not
- * MUST_NOT_MOVE buflib will move the allocation before even attempting to
- * shrink.
+ * Callbacks run by buflib to manage an allocation.
*/
-struct buflib_callbacks {
+struct buflib_callbacks
+{
/**
- * This is called before data is moved. Use this to fix up any cached
- * pointers pointing to inside the allocation. The size is unchanged.
- *
- * This is not needed if you don't cache the data pointer (but always
- * call buflib_get_data()) and don't pass pointer to the data to yielding
- * functions.
+ * \brief Called when buflib wants to move the buffer
+ * \param handle Handle being moved
+ * \param current Current address of the buffer
+ * \param new New address the buffer would have after moving
+ * \return BUFLIB_CB_OK - Allow the buffer to be moved.
+ * \return BUFLIB_CB_CANNOT_MOVE - Do not allow the buffer to be moved.
*
- * handle: The corresponding handle
- * current: The current start of the allocation
- * new: The new start of the allocation, after data movement
+ * This callback allows you to fix up any pointers that might
+ * be pointing to the buffer before it is moved. The task of
+ * actually moving the buffer contents is performed by buflib
+ * after the move callback returns, if movement is allowed.
*
- * Return: Return BUFLIB_CB_OK, or BUFLIB_CB_CANNOT_MOVE if movement
- * is impossible at this moment.
+ * Care must be taken to ensure that the buffer is not accessed
+ * from outside the move callback until the move is complete. If
+ * this is a concern, eg. due to multi-threaded access, then you
+ * must implement a sync_callback() and guard any access to the
+ * buffer with a lock.
*
- * If NULL: this allocation must not be moved around
- * by the buflib when compaction occurs. Attention: Don't confuse
- * that with passing NULL for the whole callback structure
- * to buflib_alloc_ex(). This would enable moving buffers by default.
- * You have to pass NULL inside the "struct buflib_callbacks" structure.
+ * If the move callback is NULL then buflib will never move
+ * the allocation, as if you returned BUFLIB_CB_CANNOT_MOVE.
*/
int (*move_callback)(int handle, void* current, void* new);
+
/**
- * This is called when the buflib desires to shrink a buffer
- * in order to satisfy new allocation. This happens when buflib runs
- * out of memory, e.g. because buflib_alloc_maximum() was called.
- * Move data around as you need to make space and call core_shrink() as
- * appropriate from within the callback to complete the shrink operation.
- * buflib will not move data as part of shrinking.
- *
- * hint: bit mask containing hints on how shrinking is desired (see below)
- * handle: The corresponding handle
- * start: The old start of the allocation
+ * \brief Called when buflib wants to shrink the buffer
+ * \param handle Handle to shrink
+ * \param hints Hints regarding the shrink request
+ * \param start Current address of the buffer
+ * \param size Current size of the buffer as seen by buflib.
+ * This may be rounded up compared to the nominal
+ * allocation size due to alignment requirements.
+ * \return BUFLIB_CB_OK - Was able to shrink the buffer.
+ * \return BUFLIB_CB_CANNOT_SHRINK - Buffer cannot shrink.
*
- * Return: Return BUFLIB_CB_OK, or BUFLIB_CB_CANNOT_SHRINK if shirinking
- * is impossible at this moment.
+ * This callback is run by buflib when it runs out of memory
+ * and starts a compaction run. Buflib will not actually try
+ * to shrink or move memory, you must do that yourself and
+ * call buflib_shrink() to report the new start address and
+ * size of the buffer.
*
- * if NULL: this allocation cannot be resized.
- * It is recommended that allocation that must not move are
- * at least shrinkable
+ * If the shrink callback is NULL then buflib will regard the
+ * buffer as non-shrinkable.
*/
- int (*shrink_callback)(int handle, unsigned hints, void* start, size_t old_size);
+ int (*shrink_callback)(int handle, unsigned hints,
+ void *start, size_t size);
+
/**
- * This is called when special steps must be taken for synchronization
- * both before the move_callback is called and after the data has been
- * moved.
+ * \brief Called before and after attempting to move the buffer
+ * \param handle Handle being moved
+ * \param lock True to lock, false to unlock
+ *
+ * The purpose of this callback is to block access to the buffer
+ * from other threads while a buffer is being moved, using a lock
+ * such as a mutex.
+ *
+ * It is called with `sync_callback(handle, true)` before running
+ * the move callback and `sync_callback(handle, false)` after the
+ * move is complete, regardless of whether the buffer was actually
+ * moved or not.
*/
- void (*sync_callback)(int handle, bool sync_on);
+ void (*sync_callback)(int handle, bool lock);
};
-#define BUFLIB_SHRINK_SIZE_MASK (~BUFLIB_SHRINK_POS_MASK)
-#define BUFLIB_SHRINK_POS_FRONT (1u<<31)
-#define BUFLIB_SHRINK_POS_BACK (1u<<30)
-#define BUFLIB_SHRINK_POS_MASK (BUFLIB_SHRINK_POS_FRONT|BUFLIB_SHRINK_POS_BACK)
-
/**
- * Possible return values for the callbacks, some of them can cause
- * compaction to fail and therefore new allocations to fail
+ * A set of all NULL callbacks for use with allocations that need to stay
+ * locked in RAM and not moved or shrunk. These type of allocations should
+ * be avoided as much as possible to avoid memory fragmentation but it can
+ * suitable for short-lived allocations.
+ *
+ * \note Use of this is discouraged. Prefer to use normal moveable
+ * allocations and pin them.
*/
-/* Everything alright */
-#define BUFLIB_CB_OK 0
-/* Tell buflib that moving failed. Buflib may retry to move at any point */
-#define BUFLIB_CB_CANNOT_MOVE 1
-/* Tell buflib that resizing failed, possibly future making allocations fail */
-#define BUFLIB_CB_CANNOT_SHRINK 1
+extern struct buflib_callbacks buflib_ops_locked;
/**
- * Initializes buflib with a caller allocated context instance and memory pool.
- *
- * The buflib_context instance needs to be passed to every other buflib
- * function. It's should be considered opaque, even though it is not yet
- * (that's to make inlining core_get_data() possible). The documentation
- * of the other functions will not describe the context
- * instance parameter further as it's obligatory.
- *
- * context: The new buflib instance to be initialized, allocated by the caller
- * size: The size of the memory pool
+ * \brief Intialize a buflib context
+ * \param ctx Context to initialize
+ * \param buf Buffer which will be used as the context's memory pool
+ * \param size Size of the buffer
*/
-void buflib_init(struct buflib_context *context, void *buf, size_t size);
-
+void buflib_init(struct buflib_context *ctx, void *buf, size_t size);
/**
* Returns the amount of unallocated bytes. It does not mean this amount
* can be actually allocated because they might not be contiguous.
- *
- * Returns: The number of unallocated bytes in the memory pool.
*/
size_t buflib_available(struct buflib_context *ctx);
/**
- * Returns the biggest possible allocation that can be determined to succeed.
- *
- * Returns: The amount of bytes of the biggest unallocated, contiguous region.
+ * Returns the size of the largest possible contiguous allocation, given
+ * the current state of the memory pool. A larger allocation may still
+ * succeed if compaction is able to create a larger contiguous area.
*/
size_t buflib_allocatable(struct buflib_context *ctx);
/**
- * Relocates the fields in *ctx to the new buffer position pointed to by buf.
- * This does _not_ move any data but updates the pointers. The data has
- * to be moved afterwards manually and only if this function returned true.
- *
- * This is intended to be called from within a move_callback(), for
- * buflib-on-buflib scenarios (i.e. a new buflib instance backed by a buffer
- * that was allocated by another buflib instance). Be aware that if the parent
- * move_callback() moves the underlying buffer _no_ move_callback() of the
- * underlying buffer are called.
- *
- * Returns true of the relocation was successful. If it returns false no
- * change to *ctx was made.
- */
-bool buflib_context_relocate(struct buflib_context *ctx, void *buf);
-
-/**
- * Allocates memory from buflib's memory pool
+ * \brief Relocate the buflib memory pool to a new address
+ * \param ctx Context to relocate
+ * \param buf New memory pool address
+ * \return True if relocation should proceed, false if it cannot.
*
- * size: How many bytes to allocate
+ * Updates all pointers inside the buflib context to point to a new pool
+ * address. You must call this function before moving the pool and move
+ * the data manually afterwards only if this function returns true.
*
- * This function passes NULL for the callback structure "ops", so buffers
- * are movable. Don't pass them to functions that yield().
+ * This is intended from a move_callback() in buflib-on-buflib scenarios,
+ * where the memory pool of the "inner" buflib is allocated from an "outer"
+ * buflib.
*
- * Returns: A positive integer handle identifying this allocation, or
- * a negative value on error (0 is also not a valid handle)
+ * \warning This does not run any move callbacks, so it is not safe to
+ * use if any allocations require them.
*/
-int buflib_alloc(struct buflib_context *context, size_t size);
+bool buflib_context_relocate(struct buflib_context *ctx, void *buf);
+/**
+ * \brief Allocate memory from buflib
+ * \param ctx Context to allocate from
+ * \param size Allocation size
+ * \return Handle for the allocation (> 0) or a negative value on error
+ *
+ * This is the same as calling buflib_alloc_ex() with a NULL callbacks
+ * struct. The resulting allocation can be moved by buflib; use pinning
+ * if you need to prevent moves.
+ *
+ * Note that zero is not a valid handle, and will never be returned by
+ * this function. However, this may change, and you should treat a zero
+ * or negative return value as an allocation failure.
+ */
+int buflib_alloc(struct buflib_context *ctx, size_t size);
/**
- * Allocates memory from the buflib's memory pool with additional callbacks
- * and flags
- *
- * name: A string identifier giving this allocation a name
- * size: How many bytes to allocate
- * ops: a struct with pointers to callback functions (see above).
- * if "ops" is NULL: Buffer is movable.
- *
- * Returns: A positive integer handle identifying this allocation, or
- * a negative value on error (0 is also not a valid handle)
+ * \brief Allocate memory from buflib with custom buffer ops
+ * \param ctx Context to allocate from
+ * \param size Allocation size
+ * \param ops Pointer to ops struct or NULL if no ops are needed.
+ * \return Handle for the allocation (> 0) or a negative value on error.
+ *
+ * Use this if you need to pass custom callbacks for responding to buflib
+ * move or shrink operations. Passing a NULL ops pointer means the buffer
+ * can be moved by buflib at any time.
+ *
+ * Note that zero is not a valid handle, and will never be returned by
+ * this function. However, this may change, and you should treat a zero
+ * or negative return value as an allocation failure.
*/
-int buflib_alloc_ex(struct buflib_context *ctx, size_t size, const char *name,
+int buflib_alloc_ex(struct buflib_context *ctx, size_t size,
struct buflib_callbacks *ops);
+/**
+ * \brief Attempt a maximum size allocation
+ * \param ctx Context to allocate from
+ * \param size Size of the allocation will be written here on success.
+ * \param ops Pointer to ops struct or NULL if no ops are needed.
+ * \return Handle for the allocation (> 0) or a negative value on error.
+ *
+ * Buflib will attempt to compact and shrink other allocations as much as
+ * possible and then allocate the largest contigous free area. Since this
+ * will consume effectively *all* available memory, future allocations are
+ * likely to fail.
+ *
+ * \note There is rarely any justification to use this with the core_alloc
+ * context due to the impact it has on the entire system. You should
+ * change your code if you think you need this. Of course, if you are
+ * using a private buflib context then this warning does not apply.
+ */
+int buflib_alloc_maximum(struct buflib_context *ctx,
+ size_t *size, struct buflib_callbacks *ops);
/**
- * Gets all available memory from buflib, for temporary use.
- *
- * Since this effectively makes all future allocations fail (unless
- * another allocation is freed in the meantime), you should definitely provide
- * a shrink callback if you plan to hold the buffer for a longer period. This
- * will allow buflib to permit allocations by shrinking the buffer returned by
- * this function.
- *
- * Note that this might return many more bytes than buflib_available() or
- * buflib_allocatable() return, because it aggressively compacts the pool
- * and even shrinks other allocations. However, do not depend on this behavior,
- * it may change.
- *
- * name: A string identifier giving this allocation a name
- * size: The actual size will be returned into size
- * ops: a struct with pointers to callback functions
- *
- * Returns: A positive integer handle identifying this allocation, or
- * a negative value on error (0 is also not a valid handle)
+ * \brief Reduce the size of a buflib allocation
+ * \param ctx Buflib context of the allocation
+ * \param handle Handle identifying the allocation
+ * \param newstart New start address. Must be within the current bounds
+ * of the allocation, as returned by buflib_get_data().
+ * \param new_size New size of the buffer.
+ * \return True if shrinking was successful; otherwise, returns false and
+ * does not modify the allocation.
+ *
+ * Shrinking always succeeds provided the new allocation is contained
+ * within the current allocation. A failure is always a programming
+ * error, so you need not check for it and in the future the failure
+ * case may be changed to a panic or undefined behavior with no return
+ * code.
+ *
+ * The new start address and size need not have any particular alignment,
+ * however buflib cannot work with unaligned addresses so there is rarely
+ * any purpose to creating unaligned allocations.
+ *
+ * Shrinking is typically done from a shrink_callback(), but can be done
+ * at any time if you want to reduce the size of a buflib allocation.
*/
-int buflib_alloc_maximum(struct buflib_context* ctx, const char* name,
- size_t *size, struct buflib_callbacks *ops);
+bool buflib_shrink(struct buflib_context *ctx, int handle,
+ void *newstart, size_t new_size);
/**
- * Queries the data pointer for the given handle. It's actually a cheap
- * operation, don't hesitate using it extensively.
- *
- * Notice that you need to re-query after every direct or indirect yield(),
- * because compaction can happen by other threads which may get your data
- * moved around (or you can get notified about changes by callbacks,
- * see further above).
+ * \brief Increment an allocation's pin count
+ * \param ctx Buflib context of the allocation
+ * \param handle Handle identifying the allocation
*
- * handle: The handle corresponding to the allocation
+ * The pin count acts like a reference count. Buflib will not attempt to
+ * move any buffer with a positive pin count, nor invoke any move or sync
+ * callbacks. Hence, when pinned, it is safe to hold pointers to a buffer
+ * across yields or use them for I/O.
*
- * Returns: The start pointer of the allocation
+ * Note that shrink callbacks can still be invoked for pinned handles.
*/
-#ifdef DEBUG
-void* buflib_get_data(struct buflib_context *ctx, int handle);
-#else
-static inline void* buflib_get_data(struct buflib_context *ctx, int handle)
-{
- return (void*)(ctx->handle_table[-handle].alloc);
-}
-#endif
+void buflib_pin(struct buflib_context *ctx, int handle);
/**
- * Shrink the memory allocation associated with the given handle
- * Mainly intended to be used with the shrink callback, but it can also
- * be called outside as well, e.g. to give back buffer space allocated
- * with buflib_alloc_maximum().
- *
- * Note that you must move/copy data around yourself before calling this,
- * buflib will not do this as part of shrinking.
- *
- * handle: The handle identifying this allocation
- * new_start: the new start of the allocation
- * new_size: the new size of the allocation
- *
- * Returns: true if shrinking was successful. Otherwise it returns false,
- * without having modified memory.
- *
+ * \brief Decrement an allocation's pin count
+ * \param ctx Buflib context of the allocation
+ * \param handle Handle identifying the allocation
*/
-bool buflib_shrink(struct buflib_context *ctx, int handle, void* newstart, size_t new_size);
+void buflib_unpin(struct buflib_context *ctx, int handle);
/**
- * Frees memory associated with the given handle
- *
- * Returns: 0 (to invalidate handles in one line, 0 is not a valid handle)
+ * \brief Return the pin count of an allocation
+ * \param ctx Buflib context of the allocation
+ * \param handle Handle identifying the allocation
+ * \return Current pin count; zero means the handle is not pinned.
+ */
+unsigned buflib_pin_count(struct buflib_context *ctx, int handle);
+
+/**
+ * \brief Free an allocation and return its memory to the pool
+ * \param ctx Buflib context of the allocation
+ * \param handle Handle identifying the allocation
+ * \return Always returns zero (zero is not a valid handle, so this can
+ * be used to invalidate the variable containing the handle).
*/
int buflib_free(struct buflib_context *context, int handle);
/**
- * Moves the underlying buflib buffer up by size bytes (as much as
- * possible for size == 0) without moving the end. This effectively
- * reduces the available space by taking away manageable space from the
- * front. This space is not available for new allocations anymore.
- *
- * To make space available in the front, everything is moved up.
- * It does _NOT_ call the move callbacks
- *
- *
- * size: size in bytes to move the buffer up (take away). The actual
- * bytes moved is returned in this
- * Returns: The new start of the underlying buflib buffer
+ * \brief Get a pointer to the buffer for an allocation
+ * \param ctx Buflib context of the allocation
+ * \param handle Handle identifying the allocation
+ * \return Pointer to the allocation's memory.
+ *
+ * Note that buflib can move allocations in order to free up space when
+ * making new allocations. For this reason, it's unsafe to hold a pointer
+ * to a buffer across a yield() or any other operation that can cause a
+ * context switch. This includes any function that may block, and even
+ * some functions that might not block -- eg. if a low priority thread
+ * acquires a mutex, calling mutex_unlock() may trigger a context switch
+ * to a higher-priority thread.
+ *
+ * buflib_get_data() is a very cheap operation, however, costing only
+ * a few pointer lookups. Don't hesitate to use it extensively.
+ *
+ * If you need to hold a pointer across a possible context switch, pin
+ * the handle with buflib_pin() to prevent the buffer from being moved.
+ * This is required when doing I/O into buflib allocations, for example.
*/
-void* buflib_buffer_out(struct buflib_context *ctx, size_t *size);
+#ifdef BUFLIB_DEBUG_GET_DATA
+void *buflib_get_data(struct buflib_context *ctx, int handle);
+#else
+static inline void *buflib_get_data(struct buflib_context *ctx, int handle);
+#endif
/**
- * Moves the underlying buflib buffer down by size bytes without
- * moving the end. This grows the buflib buffer by adding space to the front.
- * The new bytes are available for new allocations.
+ * \brief Get a pinned pointer to a buflib allocation
+ * \param ctx Buflib context of the allocation
+ * \param handle Handle identifying the allocation
+ * \return Pointer to the allocation's memory.
*
- * Everything is moved down, and the new free space will be in the middle.
- * It does _NOT_ call the move callbacks.
+ * Functionally equivalent to buflib_pin() followed by buflib_get_data(),
+ * but this call is more efficient and should be preferred over separate
+ * calls.
*
- * size: size in bytes to move the buffer down (new free space)
+ * To unpin the data, call buflib_put_data_pinned() and pass the pointer
+ * returned by this function.
*/
-void buflib_buffer_in(struct buflib_context *ctx, int size);
-
-/* debugging */
+static inline void *buflib_get_data_pinned(struct buflib_context *ctx, int handle);
/**
- * Returns the name, as given to buflib_alloc() and buflib_allloc_ex(), of the
- * allocation associated with the given handle. As naming allocations
- * is optional, there might be no name associated.
- *
- * handle: The handle indicating the allocation
+ * \brief Release a pinned pointer to a buflib allocation
+ * \param ctx Buflib context of the allocation
+ * \param data Pointer returned by buflib_get_data()
*
- * Returns: A pointer to the string identifier of the allocation, or NULL
- * if none was specified with buflib_alloc_ex().
+ * Decrements the pin count, allowing the buffer to be moved once the
+ * pin count drops to zero. This is more efficient than buflib_unpin()
+ * and should be preferred when you have a pointer to the buflib data.
*/
-const char* buflib_get_name(struct buflib_context *ctx, int handle);
+static inline void buflib_put_data_pinned(struct buflib_context *ctx, void *data);
/**
- * Prints an overview of all current allocations with the help
- * of the passed printer helper
- *
- * This walks only the handle table and prints only valid allocations
- *
- * Only available if BUFLIB_DEBUG_BLOCKS is defined
+ * \brief Shift allocations up to free space at the start of the pool
+ * \param ctx Context to operate on
+ * \param size Indicates number of bytes to free up, or 0 to free
+ * up as much as possible. On return, the actual number
+ * of bytes freed is written here.
+ * \return Pointer to the start of the free area
+ *
+ * If `*size` is non-zero, the actual amount of space freed up might
+ * be less than `*size`.
+ *
+ * \warning This will move data around in the pool without calling any
+ * move callbacks!
+ * \warning This function is deprecated and will eventually be removed.
*/
-void buflib_print_allocs(struct buflib_context *ctx, void (*print)(int, const char*));
+void* buflib_buffer_out(struct buflib_context *ctx, size_t *size);
/**
- * Prints an overview of all blocks in the buflib buffer, allocated
- * or unallocated, with the help of the passed printer helper
+ * \brief Shift allocations down into free space below the pool
+ * \param ctx Context to operate on
+ * \param size Number of bytes to add to the pool.
*
- * This walks the entire buffer and prints unallocated space also.
- * The output is also different from buflib_print_allocs().
+ * This operation should only be used to return memory that was previously
+ * taken from the pool with buflib_buffer_out(), by passing the same size
+ * that you got from that function.
*
- * Only available if BUFLIB_DEBUG_BLOCKS is defined
+ * \warning This will move data around in the pool without calling any
+ * move callbacks!
+ * \warning This function is deprecated and will eventually be removed.
*/
-void buflib_print_blocks(struct buflib_context *ctx, void (*print)(int, const char*));
+void buflib_buffer_in(struct buflib_context *ctx, int size);
+#ifdef BUFLIB_DEBUG_PRINT
/**
- * Gets the number of blocks in the entire buffer, allocated or unallocated
+ * Return the number of blocks in the buffer, allocated or unallocated.
*
- * Only available if BUFLIB_DEBUG_BLOCK_SIGNLE is defined
+ * Only available if BUFLIB_DEBUG_PRINT is defined.
*/
int buflib_get_num_blocks(struct buflib_context *ctx);
/**
- * Print information about a single block as indicated by block_num
- * into buf
+ * Write a string describing the block at index block_num to the
+ * provided buffer. The buffer will always be null terminated and
+ * there is no provision to detect truncation. (A 40-byte buffer
+ * is enough to contain any returned string.)
*
- * buflib_get_num_blocks() beforehand to get the total number of blocks,
- * as passing an block_num higher than that is undefined
+ * Returns false if the block index is out of bounds, and writes
+ * an empty string.
*
- * Only available if BUFLIB_DEBUG_BLOCK_SIGNLE is defined
+ * Only available if BUFLIB_DEBUG_PRINT is defined.
*/
-void buflib_print_block_at(struct buflib_context *ctx, int block_num,
- char* buf, size_t bufsize);
+bool buflib_print_block_at(struct buflib_context *ctx, int block_num,
+ char *buf, size_t bufsize);
+#endif
+#ifdef BUFLIB_DEBUG_CHECK_VALID
/**
* Check integrity of given buflib context
*/
void buflib_check_valid(struct buflib_context *ctx);
#endif
+
+#if CONFIG_BUFLIB_BACKEND == BUFLIB_BACKEND_MEMPOOL
+#include "buflib_mempool.h"
+#elif CONFIG_BUFLIB_BACKEND == BUFLIB_BACKEND_MALLOC
+#include "buflib_malloc.h"
+#endif
+
+#ifndef BUFLIB_ALLOC_OVERHEAD
+# define BUFLIB_ALLOC_OVERHEAD 0
+#endif
+
+#endif /* _BUFLIB_H_ */
diff --git a/firmware/include/buflib_malloc.h b/firmware/include/buflib_malloc.h
new file mode 100644
index 0000000000..a17c75c29a
--- /dev/null
+++ b/firmware/include/buflib_malloc.h
@@ -0,0 +1,65 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2023 Aidan MacDonald
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef _BUFLIB_MALLOC_H_
+#define _BUFLIB_MALLOC_H_
+
+#ifndef _BUFLIB_H_
+# error "include buflib.h instead"
+#endif
+
+struct buflib_malloc_handle
+{
+ void *data;
+ void *user;
+ size_t size;
+ unsigned int pin_count;
+ struct buflib_callbacks *ops;
+};
+
+struct buflib_context
+{
+ struct buflib_malloc_handle *allocs;
+ size_t num_allocs;
+
+ void *buf;
+ size_t bufsize;
+};
+
+#ifndef BUFLIB_DEBUG_GET_DATA
+static inline void *buflib_get_data(struct buflib_context *ctx, int handle)
+{
+ return ctx->allocs[handle - 1].user;
+}
+#endif
+
+static inline void *buflib_get_data_pinned(struct buflib_context *ctx, int handle)
+{
+ buflib_pin(ctx, handle);
+ return buflib_get_data(ctx, handle);
+}
+
+void _buflib_malloc_put_data_pinned(struct buflib_context *ctx, void *data);
+static inline void buflib_put_data_pinned(struct buflib_context *ctx, void *data)
+{
+ _buflib_malloc_put_data_pinned(ctx, data);
+}
+
+#endif /* _BUFLIB_MALLOC_H_ */
diff --git a/firmware/include/buflib_mempool.h b/firmware/include/buflib_mempool.h
new file mode 100644
index 0000000000..448e40963a
--- /dev/null
+++ b/firmware/include/buflib_mempool.h
@@ -0,0 +1,97 @@
+/***************************************************************************
+* __________ __ ___.
+* Open \______ \ ____ ____ | | _\_ |__ _______ ___
+* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+* \/ \/ \/ \/ \/
+* $Id$
+*
+* This is a memory allocator designed to provide reasonable management of free
+* space and fast access to allocated data. More than one allocator can be used
+* at a time by initializing multiple contexts.
+*
+* Copyright (C) 2009 Andrew Mahone
+* Copyright (C) 2011 Thomas Martitz
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+*
+* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+* KIND, either express or implied.
+*
+****************************************************************************/
+#ifndef _BUFLIB_MEMPOOL_H_
+#define _BUFLIB_MEMPOOL_H_
+
+#ifndef _BUFLIB_H_
+# error "include buflib.h instead"
+#endif
+
+#include "system.h"
+
+/* Indices used to access block fields as block[BUFLIB_IDX_XXX] */
+enum {
+ BUFLIB_IDX_LEN, /* length of the block, must come first */
+ BUFLIB_IDX_HANDLE, /* pointer to entry in the handle table */
+ BUFLIB_IDX_OPS, /* pointer to an ops struct */
+ BUFLIB_IDX_PIN, /* pin count */
+ BUFLIB_NUM_FIELDS,
+};
+
+union buflib_data
+{
+ intptr_t val; /* length of the block in n*sizeof(union buflib_data).
+ Includes buflib metadata overhead. A negative value
+ indicates block is unallocated */
+ volatile unsigned pincount; /* number of pins */
+ struct buflib_callbacks* ops; /* callback functions for move and shrink. Can be NULL */
+ char* alloc; /* start of allocated memory area */
+ union buflib_data *handle; /* pointer to entry in the handle table.
+ Used during compaction for fast lookup */
+};
+
+struct buflib_context
+{
+ union buflib_data *handle_table;
+ union buflib_data *first_free_handle;
+ union buflib_data *last_handle;
+ union buflib_data *buf_start;
+ union buflib_data *alloc_end;
+ bool compact;
+};
+
+#define BUFLIB_ALLOC_OVERHEAD (BUFLIB_NUM_FIELDS * sizeof(union buflib_data))
+
+#ifndef BUFLIB_DEBUG_GET_DATA
+static inline void *buflib_get_data(struct buflib_context *ctx, int handle)
+{
+ return (void *)ctx->handle_table[-handle].alloc;
+}
+#endif
+
+static inline union buflib_data *_buflib_get_block_header(void *data)
+{
+ union buflib_data *bd = ALIGN_DOWN(data, sizeof(*bd));
+ return bd - BUFLIB_NUM_FIELDS;
+}
+
+static inline void *buflib_get_data_pinned(struct buflib_context *ctx, int handle)
+{
+ void *data = buflib_get_data(ctx, handle);
+ union buflib_data *bd = _buflib_get_block_header(data);
+
+ bd[BUFLIB_IDX_PIN].pincount++;
+ return data;
+}
+
+static inline void buflib_put_data_pinned(struct buflib_context *ctx, void *data)
+{
+ (void)ctx;
+ union buflib_data *bd = _buflib_get_block_header(data);
+ bd[BUFLIB_IDX_PIN].pincount--;
+}
+
+#endif /* _BUFLIB_MEMPOOL_H_ */
diff --git a/firmware/include/chunk_alloc.h b/firmware/include/chunk_alloc.h
new file mode 100644
index 0000000000..ea9f6a64cb
--- /dev/null
+++ b/firmware/include/chunk_alloc.h
@@ -0,0 +1,71 @@
+/***************************************************************************
+* __________ __ ___.
+* Open \______ \ ____ ____ | | _\_ |__ _______ ___
+* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+* \/ \/ \/ \/ \/
+* $Id$
+*
+* Copyright (C) 2023 William Wilgus
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+*
+* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+* KIND, either express or implied.
+*
+****************************************************************************/
+
+#ifndef _CHUNKALLOC_H_
+#define _CHUNKALLOC_H_
+#include <stdbool.h>
+#include <string.h>
+#include "config.h"
+#include "buflib.h"
+
+#define CHUNK_ALLOC_INVALID ((size_t)-1)
+
+
+struct chunk_alloc_header
+{
+ struct buflib_context *context; /* buflib context for all allocations */
+ int chunk_handle; /* data handle of buflib allocated array of struct chunk */
+
+ size_t chunk_bytes_total; /* total bytes in current chunk */
+ size_t chunk_bytes_free; /* free bytes in current chunk */
+ size_t chunk_size; /* default chunk size */
+ size_t count; /* total chunks possible */
+ size_t current; /* current chunk in use */
+
+ struct {
+ int handle;
+ size_t min_offset;
+ size_t max_offset;
+ } cached_chunk;
+};
+
+void chunk_alloc_free(struct chunk_alloc_header *hdr);
+
+bool chunk_alloc_init(struct chunk_alloc_header *hdr,
+ struct buflib_context *ctx,
+ size_t chunk_size, size_t max_chunks);
+
+bool chunk_realloc(struct chunk_alloc_header *hdr,
+ size_t chunk_size, size_t max_chunks);
+
+void chunk_alloc_finalize(struct chunk_alloc_header *hdr);
+
+size_t chunk_alloc(struct chunk_alloc_header *hdr, size_t size); /* Returns offset */
+
+void* chunk_get_data(struct chunk_alloc_header *hdr, size_t offset); /* Returns data */
+
+void chunk_put_data(struct chunk_alloc_header *hdr, void* data, size_t offset);
+
+static inline bool chunk_alloc_is_initialized(struct chunk_alloc_header *hdr)
+{
+ return (hdr->chunk_handle > 0);
+}
+#endif
diff --git a/firmware/include/core_alloc.h b/firmware/include/core_alloc.h
index 67fe99dfdc..dc9b2036ec 100644
--- a/firmware/include/core_alloc.h
+++ b/firmware/include/core_alloc.h
@@ -5,38 +5,39 @@
#include <stdbool.h>
#include "config.h"
#include "buflib.h"
+#include "chunk_alloc.h"
/* All functions below are wrappers for functions in buflib.h, except
* they have a predefined context
*/
void core_allocator_init(void) INIT_ATTR;
-int core_alloc(const char* name, size_t size);
-int core_alloc_ex(const char* name, size_t size, struct buflib_callbacks *ops);
-int core_alloc_maximum(const char* name, size_t *size, struct buflib_callbacks *ops);
+int core_alloc(size_t size);
+int core_alloc_ex(size_t size, struct buflib_callbacks *ops);
+int core_alloc_maximum(size_t *size, struct buflib_callbacks *ops);
bool core_shrink(int handle, void* new_start, size_t new_size);
+void core_pin(int handle);
+void core_unpin(int handle);
+unsigned core_pin_count(int handle);
int core_free(int handle);
size_t core_available(void);
size_t core_allocatable(void);
-const char* core_get_name(int handle);
-#ifdef DEBUG
+
+#ifdef BUFLIB_DEBUG_CHECK_VALID
void core_check_valid(void);
#endif
/* DO NOT ADD wrappers for buflib_buffer_out/in. They do not call
* the move callbacks and are therefore unsafe in the core */
-#ifdef BUFLIB_DEBUG_BLOCKS
-void core_print_allocs(void (*print)(const char*));
-void core_print_blocks(void (*print)(const char*));
-#endif
-#ifdef BUFLIB_DEBUG_BLOCK_SINGLE
-int core_get_num_blocks(void);
-void core_print_block_at(int block_num, char* buf, size_t bufsize);
-#endif
+#ifdef BUFLIB_DEBUG_PRINT
+int core_get_num_blocks(void);
+bool core_print_block_at(int block_num, char* buf, size_t bufsize);
/* frees the debug test alloc created at initialization,
- * since this is the first any further alloc should force a compaction run */
+ * since this is the first any further alloc should force a compaction run
+ * only used if debug print is active */
bool core_test_free(void);
+#endif
static inline void* core_get_data(int handle)
{
@@ -44,4 +45,23 @@ static inline void* core_get_data(int handle)
return buflib_get_data(&core_ctx, handle);
}
+static inline void* core_get_data_pinned(int handle)
+{
+ extern struct buflib_context core_ctx;
+ return buflib_get_data_pinned(&core_ctx, handle);
+}
+
+static inline void core_put_data_pinned(void *data)
+{
+ extern struct buflib_context core_ctx;
+ buflib_put_data_pinned(&core_ctx, data);
+}
+
+/* core context chunk_alloc */
+static inline bool core_chunk_alloc_init(struct chunk_alloc_header *hdr,
+ size_t chunk_size, size_t max_chunks)
+{
+ extern struct buflib_context core_ctx;
+ return chunk_alloc_init(hdr, &core_ctx, chunk_size, max_chunks);
+}
#endif /* __CORE_ALLOC_H__ */
diff --git a/firmware/include/crc32-mi4.h b/firmware/include/crc32-mi4.h
deleted file mode 100644
index 431757271a..0000000000
--- a/firmware/include/crc32-mi4.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id: crc32.h 10464 2006-08-05 20:19:10Z miipekk $
- *
- * Copyright (C) 2007 Barry Wardell
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ****************************************************************************/
-#ifndef _CRC32_MI4_H
-#define _CRC32_MI4_H
-
-unsigned int chksum_crc32 (unsigned char *block, unsigned int length);
-#endif
diff --git a/firmware/include/crc32.h b/firmware/include/crc32.h
index 8e1f868988..adf7e2bc6e 100644
--- a/firmware/include/crc32.h
+++ b/firmware/include/crc32.h
@@ -24,6 +24,7 @@
#define _CRC32_H
uint32_t crc_32(const void *src, uint32_t len, uint32_t crc32);
+uint32_t crc_32r(const void *src, uint32_t len, uint32_t crc32);
#endif
diff --git a/firmware/include/dir.h b/firmware/include/dir.h
index 2f78b11cf5..4599877ede 100644
--- a/firmware/include/dir.h
+++ b/firmware/include/dir.h
@@ -63,6 +63,9 @@
#ifndef dir_exists
#define dir_exists FS_PREFIX(dir_exists)
#endif
+#ifndef root_realpath
+#define root_realpath FS_PREFIX(root_realpath)
+#endif
#endif /* !DIRFUNCTIONS_DEFINED */
#ifndef DIRENT_DEFINED
@@ -83,6 +86,9 @@ struct dirinfo
#ifndef DIRFUNCTIONS_DECLARED
/* TIP: set errno to zero before calling to see if anything failed */
struct dirinfo dir_get_info(DIR *dirp, struct DIRENT *entry);
+const char* root_realpath(void);
#endif /* !DIRFUNCTIONS_DECLARED */
+
+
#endif /* _DIR_H_ */
diff --git a/firmware/include/dircache_redirect.h b/firmware/include/dircache_redirect.h
index 9fae16b551..36f68b7251 100644
--- a/firmware/include/dircache_redirect.h
+++ b/firmware/include/dircache_redirect.h
@@ -20,8 +20,24 @@
****************************************************************************/
#ifndef _DIRCACHE_REDIRECT_H_
+#include "rbpaths.h"
+#include "pathfuncs.h"
#include "dir.h"
+#include "dircache.h"
+#if defined(HAVE_MULTIBOOT) && !defined(SIMULATOR) && !defined(BOOTLOADER)
+#include "rb-loader.h"
+#include "multiboot.h"
+#include "bootdata.h"
+#include "crc32.h"
+#endif
+
+#ifndef RB_ROOT_VOL_HIDDEN
+#define RB_ROOT_VOL_HIDDEN(v) (0 == 0)
+#endif
+#ifndef RB_ROOT_CONTENTS_DIR
+#define RB_ROOT_CONTENTS_DIR "/"
+#endif
/***
** Internal redirects that depend upon whether or not dircache is made
**
@@ -123,10 +139,65 @@ static inline void fileop_onsync_internal(struct filestr_base *stream)
static inline void volume_onmount_internal(IF_MV_NONVOID(int volume))
{
+#if (defined(HAVE_MULTIVOLUME) || (defined(HAVE_MULTIBOOT) && !defined(BOOTLOADER)))
+ char path[VOL_MAX_LEN+2];
+#endif
+#if defined(HAVE_MULTIBOOT) && !defined(SIMULATOR) && !defined(BOOTLOADER)
+ char rtpath[MAX_PATH / 2];
+ make_volume_root(volume, path);
+
+ unsigned int crc = crc_32(boot_data.payload, boot_data.length, 0xffffffff);
+ if (crc > 0 && crc == boot_data.crc)
+ {
+ /* we need to mount the drive before we can access it */
+ root_mount_path(path, 0); /* root could be different folder don't hide */
+
+/*BUGFIX bootloader is less selective about which drives it will mount -- revisit */
+#if defined(HAVE_MULTIDRIVE) && (NUM_VOLUMES_PER_DRIVE == 1)
+ if (volume_drive(volume) == boot_data.boot_volume
+ || volume == boot_data.boot_volume)
+#else
+ if (volume == boot_data.boot_volume) /* boot volume contained in uint8_t payload */
+#endif
+ {
+ int rtlen = get_redirect_dir(rtpath, sizeof(rtpath), volume, "", "");
+ while (rtlen > 0 && rtpath[--rtlen] == PATH_SEPCH)
+ rtpath[rtlen] = '\0'; /* remove extra separators */
+
+#if 0 /*removed, causes issues with playback for now?*/
+ if (rtlen <= 0 || rtpath[rtlen] == VOL_END_TOK)
+ root_unmount_volume(volume); /* unmount so root can be hidden*/
+#endif
+ if (rtlen <= 0) /* Error occurred, card removed? Set root to default */
+ {
+ root_unmount_volume(volume); /* unmount so root can be hidden*/
+ goto standard_redirect;
+ }
+ root_mount_path(rtpath, NSITEM_CONTENTS);
+ }
+
+ } /*CRC OK*/
+ else
+ {
+standard_redirect:
+ root_mount_path(path, RB_ROOT_VOL_HIDDEN(volume) ? NSITEM_HIDDEN : 0);
+ if (volume == path_strip_volume(RB_ROOT_CONTENTS_DIR, NULL, false))
+ root_mount_path(RB_ROOT_CONTENTS_DIR, NSITEM_CONTENTS);
+ }
+#elif defined(HAVE_MULTIVOLUME)
+ make_volume_root(volume, path);
+ root_mount_path(path, RB_ROOT_VOL_HIDDEN(volume) ? NSITEM_HIDDEN : 0);
+ if (volume == path_strip_volume(RB_ROOT_CONTENTS_DIR, NULL, false))
+ root_mount_path(RB_ROOT_CONTENTS_DIR, NSITEM_CONTENTS);
+#else
+ const char *path = PATH_ROOTSTR;
+ root_mount_path(path, RB_ROOT_VOL_HIDDEN(volume) ? NSITEM_HIDDEN : 0);
+ root_mount_path(RB_ROOT_CONTENTS_DIR, NSITEM_CONTENTS);
+#endif /* HAVE_MULTIBOOT */
+
#ifdef HAVE_DIRCACHE
dircache_mount();
#endif
- IF_MV( (void)volume; )
}
static inline void volume_onunmount_internal(IF_MV_NONVOID(int volume))
@@ -135,6 +206,7 @@ static inline void volume_onunmount_internal(IF_MV_NONVOID(int volume))
/* First, to avoid update of something about to be destroyed anyway */
dircache_unmount(IF_MV(volume));
#endif
+ root_unmount_volume(IF_MV(volume));
fileobj_mgr_unmount(IF_MV(volume));
}
@@ -152,7 +224,7 @@ static inline void fileop_onunmount_internal(struct filestr_base *stream)
static inline int readdir_dirent(struct filestr_base *stream,
struct dirscan_info *scanp,
- struct dirent *entry)
+ struct DIRENT *entry)
{
#ifdef HAVE_DIRCACHE
return dircache_readdir_dirent(stream, scanp, entry);
diff --git a/firmware/include/file.h b/firmware/include/file.h
index 040f48dfc5..02d2077977 100644
--- a/firmware/include/file.h
+++ b/firmware/include/file.h
@@ -85,6 +85,9 @@ int fdprintf(int fildes, const char *fmt, ...) ATTRIBUTE_PRINTF(2, 3);
#ifndef rename
#define rename FS_PREFIX(rename)
#endif
+#ifndef modtime
+#define modtime FS_PREFIX(modtime)
+#endif
#ifndef filesize
#define filesize FS_PREFIX(filesize)
#endif
diff --git a/firmware/include/file_internal.h b/firmware/include/file_internal.h
index d62b5a8541..ec0c5d28f0 100644
--- a/firmware/include/file_internal.h
+++ b/firmware/include/file_internal.h
@@ -30,6 +30,7 @@
#include "fs_attr.h"
#include "fs_defines.h"
#include "fat.h"
+#include "dir.h"
#ifdef HAVE_DIRCACHE
#include "dircache.h"
#endif
@@ -72,17 +73,20 @@ enum fildes_and_obj_flags
/* used in descriptor and common */
FDO_BUSY = 0x0001, /* descriptor/object is in use */
/* only used in individual stream descriptor */
- FD_WRITE = 0x0002, /* descriptor has write mode */
- FD_WRONLY = 0x0004, /* descriptor is write mode only */
- FD_APPEND = 0x0008, /* descriptor is append mode */
+ FD_VALID = 0x0002, /* descriptor is valid but not registered */
+ FD_WRITE = 0x0004, /* descriptor has write mode */
+ FD_WRONLY = 0x0008, /* descriptor is write mode only */
+ FD_APPEND = 0x0010, /* descriptor is append mode */
FD_NONEXIST = 0x8000, /* closed but not freed (uncombined) */
/* only used as common flags */
- FO_DIRECTORY = 0x0010, /* fileobj is a directory */
- FO_TRUNC = 0x0020, /* fileobj is opened to be truncated */
- FO_REMOVED = 0x0040, /* fileobj was deleted while open */
- FO_SINGLE = 0x0080, /* fileobj has only one stream open */
- FDO_MASK = 0x00ff,
- FDO_CHG_MASK = FO_TRUNC, /* fileobj permitted external change */
+ FO_DIRECTORY = 0x0020, /* fileobj is a directory */
+ FO_TRUNC = 0x0040, /* fileobj is opened to be truncated */
+ FO_REMOVED = 0x0080, /* fileobj was deleted while open */
+ FO_SINGLE = 0x0100, /* fileobj has only one stream open */
+ FO_MOUNTTARGET = 0x0200, /* fileobj kept open as a mount target */
+ FDO_MASK = 0x03ff,
+ FDO_CHG_MASK = FO_TRUNC,
+ /* fileobj permitted external change */ /* fileobj permitted external change */
/* bitflags that instruct various 'open' functions how to behave;
* saved in stream flags (only) but not used by manager */
FF_FILE = 0x00000000, /* expect file; accept file only */
@@ -95,7 +99,9 @@ enum fildes_and_obj_flags
FF_CACHEONLY = 0x00200000, /* succeed only if in dircache */
FF_INFO = 0x00400000, /* return info on self */
FF_PARENTINFO = 0x00800000, /* return info on parent */
- FF_MASK = 0x00ff0000,
+ FF_DEVPATH = 0x01000000, /* path is a device path, not root-based */
+ FF_NOFS = 0x02000000, /* no filesystem mounted here */
+ FF_MASK = 0x03ff0000,
};
/** Common data structures used throughout **/
@@ -229,10 +235,10 @@ int test_stream_exists_internal(const char *path, unsigned int callflags);
int open_noiso_internal(const char *path, int oflag); /* file.c */
void force_close_writer_internal(struct filestr_base *stream); /* file.c */
-struct dirent;
+struct DIRENT;
int uncached_readdir_dirent(struct filestr_base *stream,
struct dirscan_info *scanp,
- struct dirent *entry);
+ struct DIRENT *entry);
void uncached_rewinddir_dirent(struct dirscan_info *scanp);
int uncached_readdir_internal(struct filestr_base *stream,
@@ -333,7 +339,7 @@ static inline struct fat_direntry *get_dir_fatent(void)
void iso_decode_d_name(char *d_name);
#ifdef HAVE_DIRCACHE
-void empty_dirent(struct dirent *entry);
+void empty_dirent(struct DIRENT *entry);
void fill_dirinfo_native(struct dirinfo_native *din);
#endif /* HAVE_DIRCACHE */
diff --git a/firmware/include/fileobj_mgr.h b/firmware/include/fileobj_mgr.h
index 627d2df341..0db3520d34 100644
--- a/firmware/include/fileobj_mgr.h
+++ b/firmware/include/fileobj_mgr.h
@@ -29,6 +29,11 @@ void file_binding_remove(struct file_base_binding *bindp);
void file_binding_remove_next(struct file_base_binding *prevp,
struct file_base_binding *bindp);
+bool fileobj_mount(const struct file_base_info *srcinfop,
+ unsigned int callflags,
+ struct file_base_binding **bindpp);
+void fileobj_unmount(struct file_base_binding *bindp);
+
void fileobj_fileop_open(struct filestr_base *stream,
const struct file_base_info *srcinfop,
unsigned int callflags);
diff --git a/firmware/include/filesystem-native.h b/firmware/include/filesystem-native.h
index 640e179890..5bd61eea76 100644
--- a/firmware/include/filesystem-native.h
+++ b/firmware/include/filesystem-native.h
@@ -43,6 +43,8 @@
#define __OPEN_MODE_ARG
#define __CREAT_MODE_ARG
+#include <time.h>
+
int open(const char *name, int oflag);
int creat(const char *name);
int close(int fildes);
@@ -53,6 +55,7 @@ ssize_t read(int fildes, void *buf, size_t nbyte);
ssize_t write(int fildes, const void *buf, size_t nbyte);
int remove(const char *path);
int rename(const char *old, const char *new);
+int modtime(const char *path, time_t modtime);
off_t filesize(int fildes);
int fsamefile(int fildes1, int fildes2);
int relate(const char *path1, const char *path2);
diff --git a/firmware/include/fs_defines.h b/firmware/include/fs_defines.h
index 538c4b36cd..f4c8385d8a 100644
--- a/firmware/include/fs_defines.h
+++ b/firmware/include/fs_defines.h
@@ -45,17 +45,26 @@
/* limits for number of open descriptors - if you increase these values, make
certain that the disk cache has enough available buffers */
+
+#if MEMORYSIZE < 8
#define MAX_OPEN_FILES 11
#define MAX_OPEN_DIRS 12
+#else
+#define MAX_OPEN_FILES 31
+#define MAX_OPEN_DIRS 32
+#endif /* MEMORYSIZE */
+
/* internal functions open streams as well; make sure they don't fail if all
user descs are busy; this needs to be at least the greatest quantity needed
at once by all internal functions */
+#define MOUNT_AUX_FILEOBJS 1
#ifdef HAVE_DIRCACHE
-#define AUX_FILEOBJS 3
+#define DIRCACHE_AUX_FILEOBJS 1
#else
-#define AUX_FILEOBJS 2
+#define DIRCACHE_AUX_FILEOBJS 0
#endif
+#define AUX_FILEOBJS (2+DIRCACHE_AUX_FILEOBJS+MOUNT_AUX_FILEOBJS)
/* number of components statically allocated to handle the vast majority
of path depths; should maybe be tuned for >= 90th percentile but for now,
@@ -94,10 +103,7 @@
#if MEMORYSIZE < 8
#define DC_NUM_ENTRIES 32
#define DC_MAP_NUM_ENTRIES 128
-#elif MEMORYSIZE <= 32
-#define DC_NUM_ENTRIES 48
-#define DC_MAP_NUM_ENTRIES 128
-#else /* MEMORYSIZE > 32 */
+#else
#define DC_NUM_ENTRIES 64
#define DC_MAP_NUM_ENTRIES 256
#endif /* MEMORYSIZE */
diff --git a/firmware/include/inflate.h b/firmware/include/inflate.h
new file mode 100644
index 0000000000..1fce186f34
--- /dev/null
+++ b/firmware/include/inflate.h
@@ -0,0 +1,70 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 by James Buren (libflate adaptations for RockBox)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef _INFLATE_H_
+#define _INFLATE_H_
+
+#include <stdint.h>
+#include <stddef.h>
+
+enum {
+ INFLATE_RAW,
+ INFLATE_ZLIB,
+ INFLATE_GZIP,
+};
+
+struct inflate;
+
+typedef uint32_t (*inflate_reader) (void* block, uint32_t block_size, void* ctx);
+typedef uint32_t (*inflate_writer) (const void* block, uint32_t block_size, void* ctx);
+
+extern const uint32_t inflate_size;
+extern const uint32_t inflate_align;
+
+// 'it' must be allocated by the caller. it must be at least 'inflate_size' bytes
+// and aligned at 'inflate_align' bytes. 'st' is the type of stream to decompress;
+// see above enum for possible options.
+int inflate(struct inflate* it, int st, inflate_reader read, void* rctx, inflate_writer write, void* wctx);
+
+struct inflate_bufferctx {
+ // initialize this with your input/output buffer.
+ // the pointer is updated as data is read or written.
+ void* buf;
+
+ // buffer end marker (= buf + buf_size).
+ void* end;
+};
+
+// reader and writer for using an in-memory buffer.
+// Use 'inflate_bufferctx' as the context argument.
+uint32_t inflate_buffer_reader(void* block, uint32_t block_size, void* ctx);
+uint32_t inflate_buffer_writer(const void* block, uint32_t block_size, void* ctx);
+
+// dummy writer used if you just want to figure out how big the decompressed
+// data will be. It does not actually write any data. Example usage:
+//
+// size_t size = 0;
+// inflate(it, st, read, rctx, inflate_getsize_writer, &size);
+//
+// Now 'size' will be the size of the decompressed data (assuming no errors).
+uint32_t inflate_getsize_writer(const void* block, uint32_t block_size, void* ctx);
+
+#endif
diff --git a/firmware/include/linked_list.h b/firmware/include/linked_list.h
index c678cfa7eb..1e077be22a 100644
--- a/firmware/include/linked_list.h
+++ b/firmware/include/linked_list.h
@@ -21,6 +21,8 @@
#ifndef LINKED_LIST_H
#define LINKED_LIST_H
+#include <stddef.h>
+
/***
** NOTES:
** Node field order chosen so that one type can alias the other for forward
@@ -51,7 +53,15 @@ struct ll_node
struct ll_node *next; /* Next list item */
};
-void ll_init(struct ll_head *list);
+/**
+ * Initializes the singly-linked list
+ */
+static inline void ll_init(struct ll_head *list)
+{
+ list->head = NULL;
+ list->tail = NULL;
+}
+
void ll_insert_next(struct ll_head *list, struct ll_node *node,
struct ll_node *newnode);
void ll_insert_last(struct ll_head *list, struct ll_node *node);
@@ -81,7 +91,22 @@ struct lld_node
struct lld_node *prev; /* Previous list item */
};
-void lld_init(struct lld_head *list);
+/**
+ * Initializes the doubly-linked list
+ */
+static inline void lld_init(struct lld_head *list)
+{
+ list->head = NULL;
+ list->tail = NULL;
+
+ /* tail could be stored in first item's prev pointer but this simplifies
+ the routines and maintains the non-circularity */
+}
+
+void lld_insert_next(struct lld_head *list, struct lld_node *node,
+ struct lld_node *newnode);
+void lld_insert_prev(struct lld_head *list, struct lld_node *node,
+ struct lld_node *newnode);
void lld_insert_first(struct lld_head *list, struct lld_node *node);
void lld_insert_last(struct lld_head *list, struct lld_node *node);
void lld_remove(struct lld_head *list, struct lld_node *node);
@@ -106,7 +131,14 @@ struct lldc_node
struct lldc_node *prev; /* Previous list item */
};
-void lldc_init(struct lldc_head *list);
+/**
+ * Initializes the doubly-linked circular list
+ */
+static inline void lldc_init(struct lldc_head *list)
+{
+ list->head = NULL;
+}
+
void lldc_insert_first(struct lldc_head *list, struct lldc_node *node);
void lldc_insert_last(struct lldc_head *list, struct lldc_node *node);
void lldc_remove(struct lldc_head *list, struct lldc_node *node);
diff --git a/firmware/include/rb-loader.h b/firmware/include/rb-loader.h
index 71b6e038aa..d554ace95e 100644
--- a/firmware/include/rb-loader.h
+++ b/firmware/include/rb-loader.h
@@ -18,21 +18,9 @@
*
****************************************************************************/
+#ifndef __RB_LOADER_H__
+#define __RB_LOADER_H__
+
int load_firmware(unsigned char* buf, const char* firmware, int buffer_size);
-#ifdef HAVE_MULTIBOOT /* defined by config.h */
-/* Check in root of this <volume> for rockbox_main.<playername>
- * if this file empty or there is a single slash '/'
- * buf = '<volume#>/<rootdir>/<firmware(name)>\0'
- * If instead '/<*DIRECTORY*>' is supplied
- * addpath will be set to this DIRECTORY buf =
- * '/<volume#>/addpath/<rootdir>/<firmware(name)>\0'
- * On error returns Negative number or 0
- * On success returns bytes from snprintf
- * and generated path will be placed in buf
- * note: if supplied buffer is too small return will be
- * the number of bytes that would have been written
- */
-int get_redirect_dir(char* buf, int buffer_size, int volume,
- const char* rootdir, const char* firmware);
-#endif
+#endif /* __RB_LOADER_H__ */
diff --git a/firmware/include/rb_namespace.h b/firmware/include/rb_namespace.h
new file mode 100644
index 0000000000..5cd8c2dd29
--- /dev/null
+++ b/firmware/include/rb_namespace.h
@@ -0,0 +1,82 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2017 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.
+ *
+ ****************************************************************************/
+#ifndef RB_NAMESPACE_H
+#define RB_NAMESPACE_H
+
+#include "file_internal.h"
+
+enum ns_item_flags
+{
+ NSITEM_MOUNTED = 0x01, /* item is mounted */
+ NSITEM_HIDDEN = 0x02, /* item is not enumerated */
+ NSITEM_CONTENTS = 0x04, /* contents enumerate */
+};
+
+struct ns_scan_info
+{
+ struct dirscan_info scan; /* dirscan info - first! */
+ int item; /* current item in parent */
+};
+
+/* root functions */
+#define ROOT_MAX_REALPATH 80
+const char* root_get_realpath(void);
+int root_mount_path(const char *path, unsigned int flags);
+void root_unmount_volume(IF_MV_NONVOID(int volume));
+int root_readdir_dirent(struct filestr_base *stream,
+ struct ns_scan_info *scanp,
+ struct DIRENT *entry);
+
+/* namespace functions */
+int ns_parse_root(const char *path, const char **pathp, uint16_t *lenp);
+int ns_open_root(IF_MV(int volume,) unsigned int *callflagsp,
+ struct file_base_info *infop, uint16_t *attrp);
+int ns_open_stream(const char *path, unsigned int callflags,
+ struct filestr_base *stream, struct ns_scan_info *scanp);
+bool ns_volume_is_visible(IF_MV_NONVOID(int volume));
+
+/* closes the namespace stream */
+static inline int ns_close_stream(struct filestr_base *stream)
+{
+ return close_stream_internal(stream);
+}
+
+#include "dircache_redirect.h"
+
+static inline void ns_dirscan_rewind(struct ns_scan_info *scanp)
+{
+ rewinddir_dirent(&scanp->scan);
+ if (scanp->item != -1)
+ scanp->item = 0;
+}
+
+static inline int ns_readdir_dirent(struct filestr_base *stream,
+ struct ns_scan_info *scanp,
+ struct dirent *entry)
+
+{
+ if (scanp->item == -1)
+ return readdir_dirent(stream, &scanp->scan, entry);
+ else
+ return root_readdir_dirent(stream, scanp, entry);
+}
+
+#endif /* RB_NAMESPACE_H */
diff --git a/firmware/include/rbendian.h b/firmware/include/rbendian.h
index 8adcb544f9..8a6bb43a05 100644
--- a/firmware/include/rbendian.h
+++ b/firmware/include/rbendian.h
@@ -184,4 +184,241 @@ static inline uint32_t swaw32_hw(uint32_t value)
#error "Unknown endianness!"
#endif
+/*
+ * Generic unaligned loads
+ */
+static inline uint16_t _generic_load_le16(const void* p)
+{
+ const uint8_t* d = p;
+ return d[0] | (d[1] << 8);
+}
+
+static inline uint32_t _generic_load_le32(const void* p)
+{
+ const uint8_t* d = p;
+ return d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24);
+}
+
+static inline uint64_t _generic_load_le64(const void* p)
+{
+ const uint8_t* d = p;
+ return (((uint64_t)d[0] << 0) | ((uint64_t)d[1] << 8) |
+ ((uint64_t)d[2] << 16) | ((uint64_t)d[3] << 24) |
+ ((uint64_t)d[4] << 32) | ((uint64_t)d[5] << 40) |
+ ((uint64_t)d[6] << 48) | ((uint64_t)d[7] << 56));
+}
+
+static inline uint16_t _generic_load_be16(const void* p)
+{
+ const uint8_t* d = p;
+ return (d[0] << 8) | d[1];
+}
+
+static inline uint32_t _generic_load_be32(const void* p)
+{
+ const uint8_t* d = p;
+ return (d[0] << 24) | (d[1] << 16) | (d[2] << 8) | d[3];
+}
+
+static inline uint64_t _generic_load_be64(const void* p)
+{
+ const uint8_t* d = p;
+ return (((uint64_t)d[0] << 56) | ((uint64_t)d[1] << 48) |
+ ((uint64_t)d[2] << 40) | ((uint64_t)d[3] << 32) |
+ ((uint64_t)d[4] << 24) | ((uint64_t)d[5] << 16) |
+ ((uint64_t)d[6] << 8) | ((uint64_t)d[7] << 0));
+}
+
+static inline void _generic_store_le16(void* p, uint16_t val)
+{
+ uint8_t* d = p;
+ d[0] = val & 0xff;
+ d[1] = (val >> 8) & 0xff;
+}
+
+static inline void _generic_store_le32(void* p, uint32_t val)
+{
+ uint8_t* d = p;
+ d[0] = val & 0xff;
+ d[1] = (val >> 8) & 0xff;
+ d[2] = (val >> 16) & 0xff;
+ d[3] = (val >> 24) & 0xff;
+}
+
+static inline void _generic_store_le64(void* p, uint64_t val)
+{
+ uint8_t* d = p;
+ d[0] = val & 0xff;
+ d[1] = (val >> 8) & 0xff;
+ d[2] = (val >> 16) & 0xff;
+ d[3] = (val >> 24) & 0xff;
+ d[4] = (val >> 32) & 0xff;
+ d[5] = (val >> 40) & 0xff;
+ d[6] = (val >> 48) & 0xff;
+ d[7] = (val >> 56) & 0xff;
+}
+
+static inline void _generic_store_be16(void* p, uint16_t val)
+{
+ uint8_t* d = p;
+ d[0] = (val >> 8) & 0xff;
+ d[1] = val & 0xff;
+}
+
+static inline void _generic_store_be32(void* p, uint32_t val)
+{
+ uint8_t* d = p;
+ d[0] = (val >> 24) & 0xff;
+ d[1] = (val >> 16) & 0xff;
+ d[2] = (val >> 8) & 0xff;
+ d[3] = val & 0xff;
+}
+
+static inline void _generic_store_be64(void* p, uint64_t val)
+{
+ uint8_t* d = p;
+ d[0] = (val >> 56) & 0xff;
+ d[1] = (val >> 48) & 0xff;
+ d[2] = (val >> 40) & 0xff;
+ d[3] = (val >> 32) & 0xff;
+ d[4] = (val >> 24) & 0xff;
+ d[5] = (val >> 16) & 0xff;
+ d[6] = (val >> 8) & 0xff;
+ d[7] = val & 0xff;
+}
+
+#if !defined(HAVE_UNALIGNED_LOAD_STORE)
+
+/* Use generic unaligned loads */
+#define load_le16 _generic_load_le16
+#define load_le32 _generic_load_le32
+#define load_le64 _generic_load_le64
+#define load_be16 _generic_load_be16
+#define load_be32 _generic_load_be32
+#define load_be64 _generic_load_be64
+#define store_le16 _generic_store_le16
+#define store_le32 _generic_store_le32
+#define store_le64 _generic_store_le64
+#define store_be16 _generic_store_be16
+#define store_be32 _generic_store_be32
+#define store_be64 _generic_store_be64
+
+/* Define host byte order unaligned load */
+#if defined(ROCKBOX_LITTLE_ENDIAN)
+# define load_h16 load_le16
+# define load_h32 load_le32
+# define load_h64 load_le64
+# define store_h16 store_le16
+# define store_h32 store_le32
+# define store_h64 store_le64
+#elif defined(ROCKBOX_BIG_ENDIAN)
+# define load_h16 load_be16
+# define load_h32 load_be32
+# define load_h64 load_be64
+# define store_h16 store_be16
+# define store_h32 store_be32
+# define store_h64 store_be64
+#else
+# error
+#endif
+
+#else /* HAVE_UNALIGNED_LOAD_STORE */
+
+/* The arch should define unaligned loads in host byte order */
+#if defined(ROCKBOX_LITTLE_ENDIAN)
+# define load_le16 load_h16
+# define load_le32 load_h32
+# define load_le64 load_h64
+# define load_be16(p) swap16(load_h16((p)))
+# define load_be32(p) swap32(load_h32((p)))
+# define load_be64(p) swap64(load_h64((p)))
+# define store_le16 store_h16
+# define store_le32 store_h32
+# define store_le64 store_h64
+# define store_be16(p,v) store_h16((p),swap16((v)))
+# define store_be32(p,v) store_h32((p),swap32((v)))
+# define store_be64(p,v) store_h64((p),swap64((v)))
+#elif defined(ROCKBOX_BIG_ENDIAN)
+# define load_le16(p) swap16(load_h16((p)))
+# define load_le32(p) swap32(load_h32((p)))
+# define load_le64(p) swap64(load_h64((p)))
+# define load_be16 load_h16
+# define load_be32 load_h32
+# define load_be64 load_h64
+# define store_le16(p,v) store_h16((p),swap16((v)))
+# define store_le32(p,v) store_h32((p),swap32((v)))
+# define store_le64(p,v) store_h64((p),swap64((v)))
+# define store_be16 store_h16
+# define store_be32 store_h32
+# define store_be64 store_h64
+#else
+# error
+#endif
+
+#endif /* HAVE_UNALIGNED_LOAD_STORE */
+
+/*
+ * Aligned loads
+ */
+
+static inline uint16_t load_h16_aligned(const void* p)
+{
+ return *(const uint16_t*)p;
+}
+
+static inline uint32_t load_h32_aligned(const void* p)
+{
+ return *(const uint32_t*)p;
+}
+
+static inline uint64_t load_h64_aligned(const void* p)
+{
+ return *(const uint64_t*)p;
+}
+
+static inline void store_h16_aligned(void* p, uint16_t val)
+{
+ *(uint16_t*)p = val;
+}
+
+static inline void store_h32_aligned(void* p, uint32_t val)
+{
+ *(uint32_t*)p = val;
+}
+
+static inline void store_h64_aligned(void* p, uint64_t val)
+{
+ *(uint64_t*)p = val;
+}
+
+#if defined(ROCKBOX_LITTLE_ENDIAN)
+# define load_le16_aligned load_h16_aligned
+# define load_le32_aligned load_h32_aligned
+# define load_le64_aligned load_h64_aligned
+# define load_be16_aligned(p) swap16(load_h16_aligned((p)))
+# define load_be32_aligned(p) swap32(load_h32_aligned((p)))
+# define load_be64_aligned(p) swap64(load_h64_aligned((p)))
+# define store_le16_aligned store_h16_aligned
+# define store_le32_aligned store_h32_aligned
+# define store_le64_aligned store_h64_aligned
+# define store_be16_aligned(p,v) store_h16_aligned((p),swap16((v)))
+# define store_be32_aligned(p,v) store_h32_aligned((p),swap32((v)))
+# define store_be64_aligned(p,v) store_h64_aligned((p),swap64((v)))
+#elif defined(ROCKBOX_BIG_ENDIAN)
+# define load_le16_aligned(p) swap16(load_h16_aligned((p)))
+# define load_le32_aligned(p) swap32(load_h32_aligned((p)))
+# define load_le64_aligned(p) swap64(load_h64_aligned((p)))
+# define load_be16_aligned load_h16_aligned
+# define load_be32_aligned load_h32_aligned
+# define load_be64_aligned load_h64_aligned
+# define store_le16_aligned(p,v) store_h16_aligned((p),swap16((v)))
+# define store_le32_aligned(p,v) store_h32_aligned((p),swap32((v)))
+# define store_le64_aligned(p,v) store_h64_aligned((p),swap64((v)))
+# define store_be16_aligned store_h16_aligned
+# define store_be32_aligned store_h32_aligned
+# define store_be64_aligned store_h64_aligned
+#else
+# error "Unknown endian!"
+#endif
+
#endif /* _RBENDIAN_H_ */
diff --git a/firmware/include/string-extra.h b/firmware/include/string-extra.h
index 549a018dfc..a9b34661a7 100644
--- a/firmware/include/string-extra.h
+++ b/firmware/include/string-extra.h
@@ -25,6 +25,7 @@
#include "strlcat.h"
#include "strcasecmp.h"
#include "strcasestr.h"
+#include "strmemccpy.h"
#include "strtok_r.h"
#include "memset16.h"
diff --git a/firmware/target/arm/pnx0101/iriver-ifp7xx/adc-target.h b/firmware/include/strmemccpy.h
index a18aa77c7e..c7004610dd 100644
--- a/firmware/target/arm/pnx0101/iriver-ifp7xx/adc-target.h
+++ b/firmware/include/strmemccpy.h
@@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Copyright (C) 2006 by Barry Wardell
+ * Copyright (C) 2022 William Wilgus
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -18,14 +18,15 @@
* KIND, either express or implied.
*
****************************************************************************/
-#ifndef _ADC_TARGET_H_
-#define _ADC_TARGET_H_
-#define NUM_ADC_CHANNELS 5
-
-#define ADC_BUTTONS 0
-#define ADC_BATTERY 1
-#define ADC_BUTTON_PLAY 2
-#define ADC_UNREG_POWER ADC_BATTERY /* For compatibility */
+#ifndef __STRMEMCCPY_H__
+#define __STRMEMCCPY_H__
+/* copies src to a buffer of len bytes stopping after
+ * len or the first NULL (\0) in src
+ * NULL terminates except when len = 0
+ * If len was exceeded NULL is returned otherwise returns
+ * a pointer to the first byte following the NULL in dst.
+*/
+char * strmemccpy(char *dst, const char *src, size_t len);
#endif
diff --git a/firmware/target/arm/tcc77x/c100/adc-target.h b/firmware/include/strptokspn_r.h
index 0990d4d62d..d565118190 100644
--- a/firmware/target/arm/tcc77x/c100/adc-target.h
+++ b/firmware/include/strptokspn_r.h
@@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Copyright (C) 2007 Dave Chapman
+ * Copyright (C) 2022 William Wilgus
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -18,9 +18,9 @@
* KIND, either express or implied.
*
****************************************************************************/
-#ifndef _ADC_TARGET_H_
-#define _ADC_TARGET_H_
-#define NUM_ADC_CHANNELS 8
-#endif /* _ADC_TARGET_H_ */
+#ifndef __STRPTOKSPN_R_H__
+#define __STRPTOKSPN_R_H__
+const char *strptokspn_r(const char *ptr, const char *sep, size_t *len, const char **end);
+#endif
diff --git a/firmware/include/timefuncs.h b/firmware/include/timefuncs.h
index 4c5f0fbb42..25e041b576 100644
--- a/firmware/include/timefuncs.h
+++ b/firmware/include/timefuncs.h
@@ -24,13 +24,17 @@
#include "config.h"
#include <stdbool.h>
+#include <stdint.h>
#include "time.h"
+time_t dostime_mktime(uint16_t dosdate, uint16_t dostime);
+void dostime_localtime(time_t time, uint16_t* dosdate, uint16_t* dostime);
struct tm *get_time(void);
int set_time(const struct tm *tm);
#if CONFIG_RTC
bool valid_time(const struct tm *tm);
void set_day_of_week(struct tm *tm);
+void set_day_of_year(struct tm *tm);
#endif
#endif /* _TIMEFUNCS_H_ */
diff --git a/firmware/include/zip.h b/firmware/include/zip.h
new file mode 100644
index 0000000000..70d225cfd7
--- /dev/null
+++ b/firmware/include/zip.h
@@ -0,0 +1,68 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 by James Buren
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef _ZIP_H_
+#define _ZIP_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <time.h>
+
+enum {
+ ZIP_PASS_SHALLOW,
+ ZIP_PASS_START,
+ ZIP_PASS_DATA,
+ ZIP_PASS_END,
+};
+
+struct zip;
+
+struct zip_args {
+ uint16_t entry;
+ uint16_t entries;
+ char* name;
+ uint32_t file_size;
+ time_t mtime;
+ void* block;
+ uint32_t block_size;
+ uint32_t read_size;
+};
+
+typedef int (*zip_callback) (const struct zip_args* args, int pass, void* ctx);
+
+// open a handle for the given full file name path
+struct zip* zip_open(const char* name, bool try_mem);
+
+// quick read of only directory index
+int zip_read_shallow(struct zip* z, zip_callback cb, void* ctx);
+
+// slow read of whole archive
+// this can also pickup where a successful shallow read leftoff
+int zip_read_deep(struct zip* z, zip_callback cb, void* ctx);
+
+// extract the contents to an existing directory
+// this can also pickup where a successful shallow read leftoff
+int zip_extract(struct zip* z, const char* root, zip_callback cb, void* ctx);
+
+// returns system resources
+void zip_close(struct zip* z);
+
+#endif
diff --git a/firmware/kernel/include/mutex.h b/firmware/kernel/include/mutex.h
index 4778eb7f11..816d8040cb 100644
--- a/firmware/kernel/include/mutex.h
+++ b/firmware/kernel/include/mutex.h
@@ -36,10 +36,5 @@ struct mutex
extern void mutex_init(struct mutex *m);
extern void mutex_lock(struct mutex *m);
extern void mutex_unlock(struct mutex *m);
-#ifndef HAVE_PRIORITY_SCHEDULING
-/* Deprecated but needed for now - firmware/drivers/ata_mmc.c */
-static inline bool mutex_test(const struct mutex *m)
- { return m->blocker.thread != NULL; }
-#endif /* HAVE_PRIORITY_SCHEDULING */
#endif /* MUTEX_H */
diff --git a/firmware/kernel/include/queue.h b/firmware/kernel/include/queue.h
index 515a7e43a8..a9c3b5a93a 100644
--- a/firmware/kernel/include/queue.h
+++ b/firmware/kernel/include/queue.h
@@ -38,7 +38,7 @@
/* make sure SYS_EVENT_CLS_BITS has enough range */
/* Bit 31->|S|c...c|i...i| */
-#define SYS_EVENT ((long)(int)(1 << 31))
+#define SYS_EVENT ((long)(int)(1u << 31))
#define SYS_EVENT_CLS_BITS (3)
#define SYS_EVENT_CLS_SHIFT (31-SYS_EVENT_CLS_BITS)
#define SYS_EVENT_CLS_MASK (((1l << SYS_EVENT_CLS_BITS)-1) << SYS_EVENT_SHIFT)
@@ -58,6 +58,7 @@
#define SYS_CHARGER_CONNECTED MAKE_SYS_EVENT(SYS_EVENT_CLS_POWER, 1)
#define SYS_CHARGER_DISCONNECTED MAKE_SYS_EVENT(SYS_EVENT_CLS_POWER, 2)
#define SYS_BATTERY_UPDATE MAKE_SYS_EVENT(SYS_EVENT_CLS_POWER, 3)
+#define SYS_REBOOT MAKE_SYS_EVENT(SYS_EVENT_CLS_POWER, 4)
#define SYS_FS_CHANGED MAKE_SYS_EVENT(SYS_EVENT_CLS_FILESYS, 0)
#define SYS_HOTSWAP_INSERTED MAKE_SYS_EVENT(SYS_EVENT_CLS_PLUG, 0)
#define SYS_HOTSWAP_EXTRACTED MAKE_SYS_EVENT(SYS_EVENT_CLS_PLUG, 1)
diff --git a/firmware/kernel/include/tick.h b/firmware/kernel/include/tick.h
index 9810f4a1e5..d1933dbe9b 100644
--- a/firmware/kernel/include/tick.h
+++ b/firmware/kernel/include/tick.h
@@ -23,7 +23,7 @@
#include "config.h"
#include "system.h" /* for NULL */
-extern void init_tick(void);
+extern void init_tick(void) INIT_ATTR;
#define HZ 100 /* number of ticks per second */
diff --git a/firmware/kernel/pthread/corelock.c b/firmware/kernel/pthread/corelock.c
deleted file mode 100644
index 10b4329639..0000000000
--- a/firmware/kernel/pthread/corelock.c
+++ /dev/null
@@ -1,18 +0,0 @@
-#include <pthread.h>
-#include "kernel.h"
-
-void corelock_init(struct corelock *lk)
-{
- lk->mutex = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER;
-}
-
-void corelock_lock(struct corelock *lk)
-{
- pthread_mutex_lock(&lk->mutex);
-}
-
-
-void corelock_unlock(struct corelock *lk)
-{
- pthread_mutex_unlock(&lk->mutex);
-}
diff --git a/firmware/kernel/pthread/mutex.c b/firmware/kernel/pthread/mutex.c
deleted file mode 100644
index 49503b5d82..0000000000
--- a/firmware/kernel/pthread/mutex.c
+++ /dev/null
@@ -1,21 +0,0 @@
-#include <pthread.h>
-#include "kernel.h"
-
-void mutex_init(struct mutex *m)
-{
- pthread_mutexattr_t attr;
- pthread_mutexattr_init(&attr);
- pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
- pthread_mutex_init(&m->mutex, &attr);
- pthread_mutexattr_destroy(&attr);
-}
-
-void mutex_lock(struct mutex *m)
-{
- pthread_mutex_lock(&m->mutex);
-}
-
-void mutex_unlock(struct mutex *m)
-{
- pthread_mutex_unlock(&m->mutex);
-}
diff --git a/firmware/kernel/pthread/thread.c b/firmware/kernel/pthread/thread.c
deleted file mode 100644
index 71cbd1d136..0000000000
--- a/firmware/kernel/pthread/thread.c
+++ /dev/null
@@ -1,204 +0,0 @@
-#include <stdlib.h>
-#include <stdbool.h>
-#include <errno.h>
-#include <pthread.h>
-#include "/usr/include/semaphore.h"
-#include "thread-internal.h"
-#include "kernel.h"
-
-#define NSEC_PER_SEC 1000000000L
-static inline void timespec_add_ns(struct timespec *a, uint64_t ns)
-{
- lldiv_t q = lldiv(a->tv_nsec + ns, NSEC_PER_SEC);
- a->tv_sec += q.quot;
- a->tv_nsec = q.rem;
-}
-
-static int threads_initialized;
-
-struct thread_init_data {
- void (*function)(void);
- bool start_frozen;
- sem_t init_sem;
- struct thread_entry *entry;
-};
-
-__thread struct thread_entry *_current;
-
-unsigned int thread_self(void)
-{
- return (unsigned) pthread_self();
-}
-
-static struct thread_entry_item {
- unsigned thread_id;
- struct thread_entry *entry;
-} entry_lookup[32];
-
-
-
-static struct thread_entry_item *__find_thread_entry(unsigned thread_id)
-{
- int i;
-
- for (i = 0; i < 32; i++)
- {
- if (entry_lookup[i].thread_id == thread_id)
- return &entry_lookup[i];
- }
- return NULL;
-}
-
-static struct thread_entry *find_thread_entry(unsigned thread_id)
-{
- return __find_thread_entry(thread_id)->entry;
-}
-
-static void *trampoline(void *arg)
-{
- struct thread_init_data *data = arg;
-
- void (*thread_fn)(void) = data->function;
-
- _current = data->entry;
-
- if (data->start_frozen)
- {
- struct corelock thaw_lock;
- corelock_init(&thaw_lock);
- corelock_lock(&thaw_lock);
-
- _current->lock = &thaw_lock;
- sem_post(&data->init_sem);
- block_thread_switch(_current, _current->lock);
- _current->lock = NULL;
-
- corelock_unlock(&thaw_lock);
- }
- else
- sem_post(&data->init_sem);
-
- free(data);
- thread_fn();
-
- return NULL;
-}
-
-void thread_thaw(unsigned int thread_id)
-{
- struct thread_entry *e = find_thread_entry(thread_id);
- if (e->lock)
- {
- corelock_lock(e->lock);
- wakeup_thread(e);
- corelock_unlock(e->lock);
- }
- /* else: no lock. must be running already */
-}
-
-void init_threads(void)
-{
- struct thread_entry_item *item0 = &entry_lookup[0];
- item0->entry = calloc(1, sizeof(struct thread_entry));
- item0->thread_id = pthread_self();
-
- _current = item0->entry;
- pthread_cond_init(&item0->entry->cond, NULL);
- threads_initialized = 1;
-}
-
-
-unsigned int create_thread(void (*function)(void),
- void* stack, size_t stack_size,
- unsigned flags, const char *name
- //~ IF_PRIO(, int priority)
- IF_COP(, unsigned int core))
-{
- pthread_t retval;
-
- struct thread_init_data *data = calloc(1, sizeof(struct thread_init_data));
- struct thread_entry *entry = calloc(1, sizeof(struct thread_entry));
- struct thread_entry_item *item;
-
- if (!threads_initialized)
- abort();
-
- data->function = function;
- data->start_frozen = flags & CREATE_THREAD_FROZEN;
- data->entry = entry;
- pthread_cond_init(&entry->cond, NULL);
- entry->runnable = true;
-
- sem_init(&data->init_sem, 0, 0);
-
- if (pthread_create(&retval, NULL, trampoline, data) < 0)
- return -1;
-
- sem_wait(&data->init_sem);
-
- item = __find_thread_entry(0);
- item->thread_id = retval;
- item->entry = entry;
-
- pthread_setname_np(retval, name);
-
-
- return retval;
-}
-
-/* for block_thread(), _w_tmp() and wakeup_thread() t->lock must point
- * to a corelock instance, and this corelock must be held by the caller */
-void block_thread_switch(struct thread_entry *t, struct corelock *cl)
-{
- t->runnable = false;
- if (wait_queue_ptr(t))
- wait_queue_register(t);
- while(!t->runnable)
- pthread_cond_wait(&t->cond, &cl->mutex);
-}
-
-void block_thread_switch_w_tmo(struct thread_entry *t, int timeout,
- struct corelock *cl)
-{
- int err = 0;
- struct timespec ts;
-
- clock_gettime(CLOCK_REALTIME, &ts);
- timespec_add_ns(&ts, timeout * (NSEC_PER_SEC/HZ));
-
- t->runnable = false;
- wait_queue_register(t->wqp, t);
- while(!t->runnable && !err)
- err = pthread_cond_timedwait(&t->cond, &cl->mutex, &ts);
-
- if (err == ETIMEDOUT)
- { /* the thread timed out and was not explicitely woken up.
- * we need to do this now to mark it runnable again */
- t->runnable = true;
- /* NOTE: objects do their own removal upon timer expiration */
- }
-}
-
-unsigned int wakeup_thread(struct thread_entry *t)
-{
- if (t->wqp)
- wait_queue_remove(t);
- t->runnable = true;
- pthread_cond_signal(&t->cond);
- return THREAD_OK;
-}
-
-
-void yield(void) {}
-
-unsigned sleep(unsigned ticks)
-{
- struct timespec ts;
-
- ts.tv_sec = ticks/HZ;
- ts.tv_nsec = (ticks % HZ) * (NSEC_PER_SEC/HZ);
-
- nanosleep(&ts, NULL);
-
- return 0;
-}
diff --git a/firmware/kernel/thread.c b/firmware/kernel/thread.c
index 307be7116a..25677c79f9 100644
--- a/firmware/kernel/thread.c
+++ b/firmware/kernel/thread.c
@@ -1019,7 +1019,7 @@ void switch_thread(void)
#ifdef RB_PROFILE
profile_thread_stopped(THREAD_ID_SLOT(thread->id));
#endif
-#ifdef DEBUG
+#ifdef BUFLIB_DEBUG_CHECK_VALID
/* Check core_ctx buflib integrity */
core_check_valid();
#endif
@@ -1501,7 +1501,14 @@ static inline void boost_thread(struct thread_entry *thread, bool boost)
if ((thread->cpu_boost != 0) != boost)
{
thread->cpu_boost = boost;
+#ifdef CPU_BOOST_LOGGING
+ const char fmt[] = __FILE__" thread[%s]";
+ char pathbuf[sizeof(fmt) + 32]; /* thread name 32 */
+ snprintf(pathbuf, sizeof(pathbuf), fmt, thread->name);
+ cpu_boost_(boost, pathbuf, __LINE__);
+#else
cpu_boost(boost);
+#endif
}
}
diff --git a/firmware/libc/atoi.c b/firmware/libc/atoi.c
index 3393839b27..00617c3d13 100644
--- a/firmware/libc/atoi.c
+++ b/firmware/libc/atoi.c
@@ -26,7 +26,10 @@ int atoi (const char *str)
{
int value = 0;
int sign = 1;
-
+
+ if (!str)
+ return 0;
+
while (isspace(*str))
{
str++;
diff --git a/firmware/libc/include/string.h b/firmware/libc/include/string.h
index 9815c62805..6217fff15c 100644
--- a/firmware/libc/include/string.h
+++ b/firmware/libc/include/string.h
@@ -20,6 +20,7 @@ extern "C" {
_PTR _EXFUN(memchr,(const _PTR, int, size_t));
int _EXFUN(memcmp,(const _PTR, const _PTR, size_t));
_PTR _EXFUN(memcpy,(_PTR, const _PTR, size_t));
+_PTR _EXFUN(memccpy,(_PTR, const _PTR, int, size_t));
_PTR _EXFUN(mempcpy,(_PTR, const _PTR, size_t));
_PTR _EXFUN(memmove,(_PTR, const _PTR, size_t));
_PTR _EXFUN(memset,(_PTR, int, size_t));
@@ -39,9 +40,6 @@ size_t _EXFUN(strspn,(const char *, const char *));
char *_EXFUN(strstr,(const char *, const char *));
char *_EXFUN(strcasestr,(const char *, const char *));
-size_t strlcpy(char *dst, const char *src, size_t siz);
-size_t strlcat(char *dst, const char *src, size_t siz);
-
#ifndef _REENT_ONLY
char *_EXFUN(strtok,(char *, const char *));
#endif
diff --git a/firmware/libc/memccpy.c b/firmware/libc/memccpy.c
new file mode 100644
index 0000000000..fa9316616e
--- /dev/null
+++ b/firmware/libc/memccpy.c
@@ -0,0 +1,119 @@
+/*
+FUNCTION
+ <<memccpy>>---copy memory regions with end-token check
+ANSI_SYNOPSIS
+ #include <string.h>
+ void* memccpy(void *restrict <[out]>, const void *restrict <[in]>,
+ int <[endchar]>, size_t <[n]>);
+TRAD_SYNOPSIS
+ void *memccpy(<[out]>, <[in]>, <[endchar]>, <[n]>
+ void *<[out]>;
+ void *<[in]>;
+ int <[endchar]>;
+ size_t <[n]>;
+DESCRIPTION
+ This function copies up to <[n]> bytes from the memory region
+ pointed to by <[in]> to the memory region pointed to by
+ <[out]>. If a byte matching the <[endchar]> is encountered,
+ the byte is copied and copying stops.
+ If the regions overlap, the behavior is undefined.
+RETURNS
+ <<memccpy>> returns a pointer to the first byte following the
+ <[endchar]> in the <[out]> region. If no byte matching
+ <[endchar]> was copied, then <<NULL>> is returned.
+PORTABILITY
+<<memccpy>> is a GNU extension.
+<<memccpy>> requires no supporting OS subroutines.
+ */
+#include <stddef.h>
+#include <string.h>
+#include <limits.h>
+#include "_ansi.h" /* for _DEFUN */
+
+/* Nonzero if either X or Y is not aligned on a "long" boundary. */
+#define ROCKBOX_UNALIGNED(X, Y) \
+ (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1)))
+/* How many bytes are copied each iteration of the word copy loop. */
+#define LITTLEBLOCKSIZE (sizeof (long))
+/* Threshhold for punting to the byte copier. */
+#define TOO_SMALL(LEN) ((LEN) < LITTLEBLOCKSIZE)
+/* Macros for detecting endchar */
+#if LONG_MAX == 2147483647L
+#define DETECTNULL(X) (((X) - 0x01010101) & ~(X) & 0x80808080)
+#else
+#if LONG_MAX == 9223372036854775807L
+/* Nonzero if X (a long int) contains a NULL byte. */
+#define DETECTNULL(X) (((X) - 0x0101010101010101) & ~(X) & 0x8080808080808080)
+#else
+#error long int is not a 32bit or 64bit type.
+#endif
+#endif
+_PTR
+_DEFUN (memccpy, (dst0, src0, endchar, len0),
+ _PTR __restrict dst0 _AND
+ _CONST _PTR __restrict src0 _AND
+ int endchar0 _AND
+ size_t len0)
+{
+#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__)
+ _PTR ptr = NULL;
+ char *dst = (char *) dst0;
+ char *src = (char *) src0;
+ char endchar = endchar0 & 0xff;
+ while (len0--)
+ {
+ if ((*dst++ = *src++) == endchar)
+ {
+ ptr = dst;
+ break;
+ }
+ }
+ return ptr;
+#else
+ _PTR ptr = NULL;
+ char *dst = dst0;
+ _CONST char *src = src0;
+ long *aligned_dst;
+ _CONST long *aligned_src;
+ char endchar = endchar0 & 0xff;
+ /* If the size is small, or either SRC or DST is unaligned,
+ then punt into the byte copy loop. This should be rare. */
+ if (!TOO_SMALL(len0) && !ROCKBOX_UNALIGNED (src, dst))
+ {
+ unsigned int i;
+ unsigned long mask = 0;
+ aligned_dst = (long*)dst;
+ aligned_src = (long*)src;
+ /* The fast code reads the ASCII one word at a time and only
+ performs the bytewise search on word-sized segments if they
+ contain the search character, which is detected by XORing
+ the word-sized segment with a word-sized block of the search
+ character and then detecting for the presence of NULL in the
+ result. */
+ for (i = 0; i < LITTLEBLOCKSIZE; i++)
+ mask = (mask << 8) + endchar;
+ /* Copy one long word at a time if possible. */
+ while (len0 >= LITTLEBLOCKSIZE)
+ {
+ unsigned long buffer = (unsigned long)(*aligned_src);
+ buffer ^= mask;
+ if (DETECTNULL (buffer))
+ break; /* endchar is found, go byte by byte from here */
+ *aligned_dst++ = *aligned_src++;
+ len0 -= LITTLEBLOCKSIZE;
+ }
+ /* Pick up any residual with a byte copier. */
+ dst = (char*)aligned_dst;
+ src = (char*)aligned_src;
+ }
+ while (len0--)
+ {
+ if ((*dst++ = *src++) == endchar)
+ {
+ ptr = dst;
+ break;
+ }
+ }
+ return ptr;
+#endif /* not PREFER_SIZE_OVER_SPEED */
+}
diff --git a/firmware/libc/strcspn.c b/firmware/libc/strcspn.c
new file mode 100644
index 0000000000..ee50066ed1
--- /dev/null
+++ b/firmware/libc/strcspn.c
@@ -0,0 +1,45 @@
+/*
+FUNCTION
+ <<strcspn>>---count characters not in string
+INDEX
+ strcspn
+ANSI_SYNOPSIS
+ size_t strcspn(const char *<[s1]>, const char *<[s2]>);
+TRAD_SYNOPSIS
+ size_t strcspn(<[s1]>, <[s2]>)
+ char *<[s1]>;
+ char *<[s2]>;
+DESCRIPTION
+ This function computes the length of the initial part of
+ the string pointed to by <[s1]> which consists entirely of
+ characters <[NOT]> from the string pointed to by <[s2]>
+ (excluding the terminating null character).
+RETURNS
+ <<strcspn>> returns the length of the substring found.
+PORTABILITY
+<<strcspn>> is ANSI C.
+<<strcspn>> requires no supporting OS subroutines.
+ */
+#include <string.h>
+#include "_ansi.h" /* for _DEFUN */
+
+size_t
+_DEFUN (strcspn, (s1, s2),
+ _CONST char *s1 _AND
+ _CONST char *s2)
+{
+ _CONST char *s = s1;
+ _CONST char *c;
+ while (*s1)
+ {
+ for (c = s2; *c; c++)
+ {
+ if (*s1 == *c)
+ break;
+ }
+ if (*c)
+ break;
+ s1++;
+ }
+ return s1 - s;
+}
diff --git a/firmware/libc/strtok.c b/firmware/libc/strtok.c
deleted file mode 100644
index 9e2eddf599..0000000000
--- a/firmware/libc/strtok.c
+++ /dev/null
@@ -1,63 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2002 by Daniel Stenberg
- *
- * 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"
-
-#ifndef HAVE_STRTOK_R
-#include <stddef.h>
-#include <string.h>
-
-char *
-strtok_r(char *ptr, const char *sep, char **end)
-{
- if (!ptr)
- /* we got NULL input so then we get our last position instead */
- ptr = *end;
-
- /* pass all letters that are including in the separator string */
- while (*ptr && strchr(sep, *ptr))
- ++ptr;
-
- if (*ptr) {
- /* so this is where the next piece of string starts */
- char *start = ptr;
-
- /* set the end pointer to the first byte after the start */
- *end = start + 1;
-
- /* scan through the string to find where it ends, it ends on a
- null byte or a character that exists in the separator string */
- while (**end && !strchr(sep, **end))
- ++*end;
-
- if (**end) {
- /* the end is not a null byte */
- **end = '\0'; /* zero terminate it! */
- ++*end; /* advance last pointer to beyond the null byte */
- }
-
- return start; /* return the position where the string starts */
- }
-
- /* we ended up on a null byte, there are no more strings to find! */
- return NULL;
-}
-
-#endif /* this was only compiled if strtok_r wasn't present */
diff --git a/firmware/linuxboot.c b/firmware/linuxboot.c
new file mode 100644
index 0000000000..6803bb466b
--- /dev/null
+++ b/firmware/linuxboot.c
@@ -0,0 +1,335 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2022 Aidan MacDonald
+ *
+ * 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 "linuxboot.h"
+#include "system.h"
+#include "core_alloc.h"
+#include "crc32.h"
+#include "inflate.h"
+#include "file.h"
+#include <string.h>
+
+/* compression support options - can be decided per target if needed,
+ * for now default to enabling everything */
+#define HAVE_UIMAGE_COMP_NONE
+#define HAVE_UIMAGE_COMP_GZIP
+
+enum {
+ E_OUT_OF_MEMORY = -1,
+ E_BUFFER_OVERFLOW = -2,
+ E_MAGIC_MISMATCH = -3,
+ E_HCRC_MISMATCH = -4,
+ E_DCRC_MISMATCH = -5,
+ E_UNSUPPORTED_COMPRESSION = -6,
+ E_READ = -7,
+ E_INFLATE = -8,
+ E_INFLATE_UNCONSUMED = -9,
+};
+
+uint32_t uimage_crc(uint32_t crc, const void* data, size_t size)
+{
+ return letoh32(crc_32r(data, size, htole32(crc ^ 0xffffffff))) ^ 0xffffffff;
+}
+
+uint32_t uimage_calc_hcrc(const struct uimage_header* uh)
+{
+ struct uimage_header h = *uh;
+ uimage_set_hcrc(&h, 0);
+ return uimage_crc(0, &h, sizeof(h));
+}
+
+static int uimage_check_header(const struct uimage_header* uh)
+{
+ if(uimage_get_magic(uh) != IH_MAGIC)
+ return E_MAGIC_MISMATCH;
+
+ if(uimage_get_hcrc(uh) != uimage_calc_hcrc(uh))
+ return E_HCRC_MISMATCH;
+
+ return 0;
+}
+
+static int uimage_alloc_state(const struct uimage_header* uh)
+{
+ size_t size;
+
+ switch(uimage_get_comp(uh)) {
+#ifdef HAVE_UIMAGE_COMP_NONE
+ case IH_COMP_NONE:
+ return 0;
+#endif
+
+#ifdef HAVE_UIMAGE_COMP_GZIP
+ case IH_COMP_GZIP:
+ size = inflate_size + inflate_align - 1;
+ return core_alloc_ex(size, &buflib_ops_locked);
+#endif
+
+ default:
+ return E_UNSUPPORTED_COMPRESSION;
+ }
+}
+
+#ifdef HAVE_UIMAGE_COMP_GZIP
+struct uimage_inflatectx
+{
+ uimage_reader reader;
+ void* rctx;
+ uint32_t dcrc;
+ size_t remain;
+ int err;
+};
+
+static uint32_t uimage_inflate_reader(void* block, uint32_t block_size, void* ctx)
+{
+ struct uimage_inflatectx* c = ctx;
+ ssize_t len = c->reader(block, block_size, c->rctx);
+ if(len < 0) {
+ c->err = E_READ;
+ return 0;
+ }
+
+ len = MIN(c->remain, (size_t)len);
+ c->remain -= len;
+ c->dcrc = uimage_crc(c->dcrc, block, len);
+ return len;
+}
+
+static int uimage_decompress_gzip(const struct uimage_header* uh, int state_h,
+ void* out, size_t* out_size,
+ uimage_reader reader, void* rctx)
+{
+ size_t hbufsz = inflate_size + inflate_align - 1;
+ void* hbuf = core_get_data(state_h);
+ ALIGN_BUFFER(hbuf, hbufsz, inflate_align);
+
+ struct uimage_inflatectx r_ctx;
+ r_ctx.reader = reader;
+ r_ctx.rctx = rctx;
+ r_ctx.dcrc = 0;
+ r_ctx.remain = uimage_get_size(uh);
+ r_ctx.err = 0;
+
+ struct inflate_bufferctx w_ctx;
+ w_ctx.buf = out;
+ w_ctx.end = out + *out_size;
+
+ int ret = inflate(hbuf, INFLATE_GZIP,
+ uimage_inflate_reader, &r_ctx,
+ inflate_buffer_writer, &w_ctx);
+ if(ret) {
+ if(r_ctx.err)
+ return r_ctx.err;
+ else if(w_ctx.end == w_ctx.buf)
+ return E_BUFFER_OVERFLOW;
+ else
+ /* note: this will likely mask DCRC_MISMATCH errors */
+ return E_INFLATE;
+ }
+
+ if(r_ctx.remain > 0)
+ return E_INFLATE_UNCONSUMED;
+ if(r_ctx.dcrc != uimage_get_dcrc(uh))
+ return E_DCRC_MISMATCH;
+
+ *out_size = w_ctx.end - w_ctx.buf;
+ return 0;
+}
+#endif /* HAVE_UIMAGE_COMP_GZIP */
+
+static int uimage_decompress(const struct uimage_header* uh, int state_h,
+ void* out, size_t* out_size,
+ uimage_reader reader, void* rctx)
+{
+ size_t in_size = uimage_get_size(uh);
+ ssize_t len;
+
+ switch(uimage_get_comp(uh)) {
+#ifdef HAVE_UIMAGE_COMP_NONE
+ case IH_COMP_NONE:
+ if(*out_size < in_size)
+ return E_BUFFER_OVERFLOW;
+
+ len = reader(out, in_size, rctx);
+ if(len < 0 || (size_t)len != in_size)
+ return E_READ;
+
+ if(uimage_crc(0, out, in_size) != uimage_get_dcrc(uh))
+ return E_DCRC_MISMATCH;
+
+ *out_size = in_size;
+ break;
+#endif
+
+#ifdef HAVE_UIMAGE_COMP_GZIP
+ case IH_COMP_GZIP:
+ return uimage_decompress_gzip(uh, state_h, out, out_size, reader, rctx);
+#endif
+
+ default:
+ return E_UNSUPPORTED_COMPRESSION;
+ }
+
+ return 0;
+}
+
+int uimage_load(struct uimage_header* uh, size_t* out_size,
+ uimage_reader reader, void* rctx)
+{
+ if(reader(uh, sizeof(*uh), rctx) != (ssize_t)sizeof(*uh))
+ return E_READ;
+
+ int ret = uimage_check_header(uh);
+ if(ret)
+ return ret;
+
+ int state_h = uimage_alloc_state(uh);
+ if(state_h < 0)
+ return E_OUT_OF_MEMORY;
+
+ *out_size = 0;
+ int out_h = core_alloc_maximum(out_size, &buflib_ops_locked);
+ if(out_h <= 0) {
+ ret = E_OUT_OF_MEMORY;
+ goto err;
+ }
+
+ ret = uimage_decompress(uh, state_h, core_get_data(out_h), out_size,
+ reader, rctx);
+ if(ret)
+ goto err;
+
+ core_shrink(out_h, NULL, *out_size);
+ ret = 0;
+
+ err:
+ core_free(state_h);
+ if(out_h > 0) {
+ if(ret == 0)
+ ret = out_h;
+ else
+ core_free(out_h);
+ }
+
+ return ret;
+}
+
+ssize_t uimage_fd_reader(void* buf, size_t size, void* ctx)
+{
+ int fd = (intptr_t)ctx;
+ return read(fd, buf, size);
+}
+
+/* Linux's self-extracting kernels are broken on MIPS. The decompressor stub
+ * doesn't flush caches after extracting the kernel code which can cause the
+ * boot to fail horribly. This has been true since at least 2009 and at the
+ * time of writing (2022) it's *still* broken.
+ *
+ * The FiiO M3K and Shanling Q1 both have broken kernels of this type, so we
+ * work around this by replacing the direct call to the kernel entry point with
+ * a thunk that adds the necessary cache flush.
+ */
+uint32_t mips_linux_stub_get_entry(void** code_start, size_t code_size)
+{
+ /* The jump to the kernel entry point looks like this:
+ *
+ * move a0, s0
+ * move a1, s1
+ * move a2, s2
+ * move a3, s3
+ * ...
+ * la k0, KERNEL_ENTRY
+ * jr k0
+ * --- or in kernels since 2021: ---
+ * la t9, KERNEL_ENTRY
+ * jalr t9
+ *
+ * We're trying to identify this code and decode the kernel entry
+ * point address, and return a suitable address where we can patch
+ * in a call to our thunk.
+ */
+
+ /* We should only need to scan within the first 128 bytes
+ * but do up to 256 just in case. */
+ uint32_t* start = *code_start;
+ uint32_t* end = start + (MIN(code_size, 256) + 3) / 4;
+
+ /* Scan for the "move aN, sN" sequence */
+ uint32_t* move_instr = start;
+ for(move_instr += 4; move_instr < end; ++move_instr) {
+ if(move_instr[-4] == 0x02002021 && /* move a0, s0 */
+ move_instr[-3] == 0x02202821 && /* move a1, s1 */
+ move_instr[-2] == 0x02403021 && /* move a2, s2 */
+ move_instr[-1] == 0x02603821) /* move a3, s3 */
+ break;
+ }
+
+ if(move_instr == end)
+ return 0;
+
+ /* Now search forward for the next jr/jalr instruction */
+ int jreg = 0;
+ uint32_t* jump_instr = move_instr;
+ for(; jump_instr != end; ++jump_instr) {
+ if((jump_instr[0] & 0xfc1ff83f) == 0xf809 ||
+ (jump_instr[0] & 0xfc00003f) == 0x8) {
+ /* jalr rN */
+ jreg = (jump_instr[0] >> 21) & 0x1f;
+ break;
+ }
+ }
+
+ /* Need room here for 4 instructions. Assume everything between the
+ * moves and the jump is safe to overwrite; otherwise, we'll need to
+ * take a different approach.
+ *
+ * Count +1 instruction for the branch delay slot and another +1 because
+ * "move_instr" points to the instruction following the last move. */
+ if(jump_instr - move_instr + 2 < 4)
+ return 0;
+ if(!jreg)
+ return 0;
+
+ /* Now scan from the end of the move sequence until the jump instruction
+ * and try to reconstruct the entry address. We check for lui/ori/addiu. */
+ const uint32_t lui_mask = 0xffff0000;
+ const uint32_t lui = 0x3c000000 | (jreg << 16);
+ const uint32_t ori_mask = 0xffff0000;
+ const uint32_t ori = 0x34000000 | (jreg << 21) | (jreg << 16);
+ const uint32_t addiu_mask = 0xffff0000;
+ const uint32_t addiu = 0x24000000 | (jreg << 21) | (jreg << 16);
+
+ /* Can use any initial value here */
+ uint32_t jreg_val = 0xdeadbeef;
+
+ for(uint32_t* instr = move_instr; instr != jump_instr; ++instr) {
+ if((instr[0] & lui_mask) == lui)
+ jreg_val = (instr[0] & 0xffff) << 16;
+ else if((instr[0] & ori_mask) == ori)
+ jreg_val |= instr[0] & 0xffff;
+ else if((instr[0] & addiu_mask) == addiu)
+ jreg_val += instr[0] & 0xffff;
+ }
+
+ /* Success! Probably! */
+ *code_start = move_instr;
+ return jreg_val;
+}
diff --git a/firmware/logf.c b/firmware/logf.c
index 11ffa4e15c..971c2d3a35 100644
--- a/firmware/logf.c
+++ b/firmware/logf.c
@@ -61,7 +61,7 @@ static int logdiskfindex;
#ifdef ROCKBOX_HAS_LOGF
#ifndef __PCTOOL__
-unsigned char logfbuffer[MAX_LOGF_SIZE];
+unsigned char logfbuffer[MAX_LOGF_SIZE + 1];
int logfindex;
bool logfwrap;
bool logfenabled = true;
@@ -272,10 +272,14 @@ void logf_panic_dump(int *y)
return;
}
+ /* Explicitly null-terminate our buffer */
+ logfbuffer[MAX_LOGF_SIZE] = 0;
+
lcd_puts(1, (*y)++, "start of logf data");
lcd_update();
- i = logfindex - 2; /* The last actual characer (i.e. not '\0') */
+ /* The intent is to dump the newest log entries first! */
+ i = logfindex - 2; /* The last actual characer (i.e. not '\0') */
while(i >= 0)
{
while(logfbuffer[i] != 0 && i>=0)
@@ -300,12 +304,13 @@ void logf_panic_dump(int *y)
}
if(strlen( &logfbuffer[i + 1]) > 0)
{
- lcd_putsf(1, (*y)++, "%*s", (MAX_LOGF_SIZE-i) &logfbuffer[i + 1]);
+ lcd_putsf(1, (*y)++, "%*s", (MAX_LOGF_SIZE-i), &logfbuffer[i + 1]);
lcd_update();
}
}
i--;
}
+
lcd_puts(1, (*y)++, "end of logf data");
lcd_update();
}
diff --git a/firmware/panic.c b/firmware/panic.c
index fcfa8b2bb8..d7f330caf1 100644
--- a/firmware/panic.c
+++ b/firmware/panic.c
@@ -32,12 +32,18 @@
#include "system.h"
#include "logf.h"
-#if defined(CPU_ARM)
+#ifdef HAVE_RB_BACKTRACE
#include "gcc_extensions.h"
-#include <backtrace.h>
+#include "backtrace.h"
#endif
+#if (defined(CPU_MIPS) && (CONFIG_PLATFORM & PLATFORM_NATIVE))
+/* TODO: see comment above exception_dump in system-mips.c */
+char panic_buf[128];
+#else
static char panic_buf[128];
+#endif
+
#define LINECHARS (LCD_WIDTH/SYSFONT_WIDTH) - 2
#if defined(CPU_ARM)
@@ -65,6 +71,12 @@ void panicf_f( const char *fmt, ...)
);
int pc = (int)__builtin_return_address(0);
+#elif defined(BACKTRACE_MIPSUNWINDER)
+void panicf( const char *fmt, ... )
+{
+ /* NOTE: these are obtained by the backtrace lib */
+ const int pc = 0;
+ const int sp = 0;
#else
void panicf( const char *fmt, ...)
{
diff --git a/firmware/pcm.c b/firmware/pcm.c
index 6fc0b626f7..de01af484f 100644
--- a/firmware/pcm.c
+++ b/firmware/pcm.c
@@ -23,7 +23,7 @@
#include "kernel.h"
/* Define LOGF_ENABLE to enable logf output in this file */
-/*#define LOGF_ENABLE*/
+//#define LOGF_ENABLE
#include "logf.h"
#include "audio.h"
#include "sound.h"
@@ -314,7 +314,7 @@ void pcm_play_stop(void)
* what pcm_apply_settings will set */
void pcm_set_frequency(unsigned int samplerate)
{
- logf("pcm_set_frequency");
+ logf("pcm_set_frequency %u", samplerate);
int index;
diff --git a/firmware/pcm_sw_volume.c b/firmware/pcm_sw_volume.c
index e972896321..bee1559bb8 100644
--- a/firmware/pcm_sw_volume.c
+++ b/firmware/pcm_sw_volume.c
@@ -26,6 +26,16 @@
#include "fixedpoint.h"
#include "pcm_sw_volume.h"
+/*
+ * NOTE: With the addition of 32-bit software scaling to this
+ * file, sometimes the "size" variable gets a little confusing.
+ *
+ * The source buffer (as of right now) is always 16-bit, and the
+ * destination buffer can potentially be 32-bit. I've tried to
+ * make it consistent: when passed in a function call, try to use
+ * the source buffer (16-bit) size.
+ */
+
/* volume factors set by pcm_set_master_volume */
static uint32_t vol_factor_l = 0, vol_factor_r = 0;
@@ -39,6 +49,25 @@ static uint32_t pcm_new_factor_l = 0, pcm_new_factor_r = 0;
static uint32_t pcm_factor_l = 0, pcm_factor_r = 0;
static typeof (memcpy) *pcm_scaling_fn = NULL;
+/* take care of some defines for 32-bit software vol */
+#if (PCM_NATIVE_BITDEPTH > 16) /* >16-bit */
+
+# define HAVE_SWVOL_32
+# define PCM_VOL_SAMPLE_SIZE (2 * sizeof (int32_t))
+# define PCM_DBL_BUF_SIZE_T int32_t
+
+# if !defined(PCM_DC_OFFSET_VALUE)
+/* PCM_DC_OFFSET_VALUE is only needed due to hardware quirk on Eros Q */
+# define PCM_DC_OFFSET_VALUE 0
+# endif
+
+#else /* 16-BIT */
+
+# define PCM_VOL_SAMPLE_SIZE PCM_SAMPLE_SIZE
+# define PCM_DBL_BUF_SIZE_T int16_t
+
+#endif /* 16-BIT */
+
/***
** Volume scaling routines
** If unbuffered, called externally by pcm driver
@@ -51,53 +80,56 @@ static typeof (memcpy) *pcm_scaling_fn = NULL;
#define PCM_F_T int64_t /* Requires large integer math */
#endif /* PCM_SW_VOLUME_FRACBITS */
-/* Scale and round sample by PCM factor */
+/* Scale sample by PCM factor */
static inline int32_t pcm_scale_sample(PCM_F_T f, int32_t s)
{
- return (f * s + (PCM_F_T)PCM_FACTOR_UNITY/2) >> PCM_SW_VOLUME_FRACBITS;
+#if defined(HAVE_SWVOL_32)
+ return (f * s + PCM_DC_OFFSET_VALUE) >> (32 - PCM_NATIVE_BITDEPTH);
+#else
+ return (f * s) >> PCM_SW_VOLUME_FRACBITS;
+#endif
}
-/* Both UNITY, use direct copy */
-/* static void * memcpy(void *dst, const void *src, size_t size); */
-
/* Either cut (both <= UNITY), no clipping needed */
-static void * pcm_scale_buffer_cut(void *dst, const void *src, size_t size)
+static void * pcm_scale_buffer_cut(void *dst, const void *src, size_t src_size)
{
- int16_t *d = dst;
+ PCM_DBL_BUF_SIZE_T *d = dst;
const int16_t *s = src;
uint32_t factor_l = pcm_factor_l, factor_r = pcm_factor_r;
- while (size)
+ while (src_size)
{
*d++ = pcm_scale_sample(factor_l, *s++);
*d++ = pcm_scale_sample(factor_r, *s++);
- size -= PCM_SAMPLE_SIZE;
+ src_size -= PCM_SAMPLE_SIZE;
}
return dst;
}
+#if !defined(HAVE_SWVOL_32) /* NOTE: 32-bit scaling is hardcoded to the cut function! */
/* Either boost (any > UNITY) requires clipping */
-static void * pcm_scale_buffer_boost(void *dst, const void *src, size_t size)
+static void * pcm_scale_buffer_boost(void *dst, const void *src, size_t src_size)
{
int16_t *d = dst;
const int16_t *s = src;
uint32_t factor_l = pcm_factor_l, factor_r = pcm_factor_r;
- while (size)
+ while (src_size)
{
*d++ = clip_sample_16(pcm_scale_sample(factor_l, *s++));
*d++ = clip_sample_16(pcm_scale_sample(factor_r, *s++));
- size -= PCM_SAMPLE_SIZE;
+ src_size -= PCM_SAMPLE_SIZE;
}
return dst;
}
+#endif
/* Transition the volume change smoothly across a frame */
-static void * pcm_scale_buffer_trans(void *dst, const void *src, size_t size)
+static void * pcm_scale_buffer_trans(void *dst, const void *src, size_t src_size)
{
- int16_t *d = dst;
+ PCM_DBL_BUF_SIZE_T *d = dst;
const int16_t *s = src;
uint32_t factor_l = pcm_factor_l, factor_r = pcm_factor_r;
@@ -110,13 +142,19 @@ static void * pcm_scale_buffer_trans(void *dst, const void *src, size_t size)
int32_t diff_l = (int32_t)new_factor_l - (int32_t)factor_l;
int32_t diff_r = (int32_t)new_factor_r - (int32_t)factor_r;
- for (size_t done = 0; done < size; done += PCM_SAMPLE_SIZE)
+ for (size_t done = 0; done < src_size; done += PCM_SAMPLE_SIZE)
{
- int32_t sweep = (1 << 14) - fp14_cos(180*done / size); /* 0.0..2.0 */
+ int32_t sweep = (1 << 14) - fp14_cos(180*done / src_size); /* 0.0..2.0 */
uint32_t f_l = fp_mul(sweep, diff_l, 15) + factor_l;
uint32_t f_r = fp_mul(sweep, diff_r, 15) + factor_r;
+#if defined(HAVE_SWVOL_32)
+ /* do not clip to 16 bits */
+ *d++ = pcm_scale_sample(f_l, *s++);
+ *d++ = pcm_scale_sample(f_r, *s++);
+#else
*d++ = clip_sample_16(pcm_scale_sample(f_l, *s++));
*d++ = clip_sample_16(pcm_scale_sample(f_r, *s++));
+#endif
}
/* Select steady-state operation */
@@ -129,9 +167,9 @@ static void * pcm_scale_buffer_trans(void *dst, const void *src, size_t size)
#ifndef PCM_SW_VOLUME_UNBUFFERED
static inline
#endif
-void pcm_sw_volume_copy_buffer(void *dst, const void *src, size_t size)
+void pcm_sw_volume_copy_buffer(void *dst, const void *src, size_t src_size)
{
- pcm_scaling_fn(dst, src, size);
+ pcm_scaling_fn(dst, src, src_size);
}
/* Assign the new scaling function for normal steady-state operation */
@@ -143,6 +181,13 @@ void pcm_sync_pcm_factors(void)
pcm_factor_l = new_factor_l;
pcm_factor_r = new_factor_r;
+/* NOTE: 32-bit scaling is limited to 0 db <--> -74 db, we will hardcode to cut.
+ * MEMCPY CANNOT BE USED, because we do need to at minimum multiply each
+ * sample up to 32-bit size. */
+#if defined(HAVE_SWVOL_32)
+ pcm_scaling_fn = pcm_scale_buffer_cut;
+#else
+
if (new_factor_l == PCM_FACTOR_UNITY &&
new_factor_r == PCM_FACTOR_UNITY)
{
@@ -157,6 +202,7 @@ void pcm_sync_pcm_factors(void)
{
pcm_scaling_fn = pcm_scale_buffer_boost;
}
+#endif
}
#ifndef PCM_SW_VOLUME_UNBUFFERED
@@ -164,10 +210,10 @@ void pcm_sync_pcm_factors(void)
static const void * volatile src_buf_addr = NULL;
static size_t volatile src_buf_rem = 0;
-#define PCM_PLAY_DBL_BUF_SIZE (PCM_PLAY_DBL_BUF_SAMPLE*PCM_SAMPLE_SIZE)
+#define PCM_PLAY_DBL_BUF_SIZE (PCM_PLAY_DBL_BUF_SAMPLE*PCM_VOL_SAMPLE_SIZE)
/* double buffer and frame length control */
-static int16_t pcm_dbl_buf[2][PCM_PLAY_DBL_BUF_SAMPLES*2]
+static PCM_DBL_BUF_SIZE_T pcm_dbl_buf[2][PCM_PLAY_DBL_BUF_SAMPLES*2]
PCM_DBL_BUF_BSS MEM_ALIGN_ATTR;
static size_t pcm_dbl_buf_size[2];
static int pcm_dbl_buf_num = 0;
@@ -205,11 +251,12 @@ bool pcm_play_dma_complete_callback(enum pcm_dma_status status,
in one chunk */
static void update_frame_params(size_t size)
{
- int count = size / PCM_SAMPLE_SIZE;
+ /* multiply by 2 for 32 bit, optimize away to 1 for 16 bit */
+ int count = (size * (sizeof(PCM_DBL_BUF_SIZE_T)/sizeof(int16_t))) / PCM_VOL_SAMPLE_SIZE;
frame_count = (count + PCM_PLAY_DBL_BUF_SAMPLES - 1) /
PCM_PLAY_DBL_BUF_SAMPLES;
int perframe = count / frame_count;
- frame_size = perframe * PCM_SAMPLE_SIZE;
+ frame_size = perframe * PCM_VOL_SAMPLE_SIZE;
frame_frac = count - perframe * frame_count;
frame_err = 0;
}
@@ -221,7 +268,8 @@ pcm_play_dma_status_callback_int(enum pcm_dma_status status)
if (status != PCM_DMAST_STARTED)
return status;
- size_t size = pcm_dbl_buf_size[pcm_dbl_buf_num];
+ /* divide by 2 for 32 bit, optimize away to 1 for 16 bit */
+ size_t size = pcm_dbl_buf_size[pcm_dbl_buf_num] / (sizeof(PCM_DBL_BUF_SIZE_T)/sizeof(int16_t));
const void *addr = src_buf_addr + size;
size = src_buf_rem - size;
@@ -237,7 +285,8 @@ pcm_play_dma_status_callback_int(enum pcm_dma_status status)
if (size != 0)
{
- size = frame_size;
+ /* multiply by 2 for 32 bit, optimize away to 1 for 16 bit */
+ size = frame_size / (sizeof(PCM_DBL_BUF_SIZE_T)/sizeof(int16_t));
if ((frame_err += frame_frac) >= frame_count)
{
@@ -247,7 +296,8 @@ pcm_play_dma_status_callback_int(enum pcm_dma_status status)
}
pcm_dbl_buf_num ^= 1;
- pcm_dbl_buf_size[pcm_dbl_buf_num] = size;
+ /* multiply by 2 for 32 bit, optimize away to 1 for 16 bit */
+ pcm_dbl_buf_size[pcm_dbl_buf_num] = size * (sizeof(PCM_DBL_BUF_SIZE_T)/sizeof(int16_t));
pcm_sw_volume_copy_buffer(pcm_dbl_buf[pcm_dbl_buf_num], addr, size);
return PCM_DMAST_OK;
@@ -265,6 +315,7 @@ static void start_pcm(bool reframe)
if (reframe)
update_frame_params(src_buf_rem);
+
pcm_play_dma_status_callback(PCM_DMAST_STARTED);
pcm_play_dma_status_callback(PCM_DMAST_STARTED);
@@ -274,7 +325,8 @@ static void start_pcm(bool reframe)
void pcm_play_dma_start_int(const void *addr, size_t size)
{
src_buf_addr = addr;
- src_buf_rem = size;
+ /* divide by 2 for 32 bit, optimize away to 1 for 16 bit */
+ src_buf_rem = size / (sizeof(PCM_DBL_BUF_SIZE_T)/sizeof(int16_t));
start_pcm(true);
}
@@ -285,23 +337,6 @@ void pcm_play_dma_stop_int(void)
src_buf_rem = 0;
}
-/* Return playing buffer from the source buffer */
-const void * pcm_play_dma_get_peak_buffer_int(int *count)
-{
- const void *addr = src_buf_addr;
- size_t size = src_buf_rem;
- const void *addr2 = src_buf_addr;
-
- if (addr == addr2 && size)
- {
- *count = size / PCM_SAMPLE_SIZE;
- return addr;
- }
-
- *count = 0;
- return NULL;
-}
-
#endif /* PCM_SW_VOLUME_UNBUFFERED */
@@ -312,13 +347,32 @@ static uint32_t pcm_centibels_to_factor(int volume)
{
if (volume == PCM_MUTE_LEVEL)
return 0; /* mute */
-
+#if defined(HAVE_SWVOL_32)
+ /*
+ * 32-bit software volume taken from pcm-alsa.c
+ */
+ volume += 48; /* -42dB .. 0dB => 5dB .. 48dB */
+ /* NOTE if vol_dB = 5 then vol_shift = 1 but r = 1 so we do vol_shift - 1 >= 0
+ * otherwise vol_dB >= 0 implies vol_shift >= 2 so vol_shift - 2 >= 0 */
+ int vol_shift = volume / 3;
+ int r = volume % 3;
+ int32_t dig_vol_mult;
+ if(r == 0)
+ dig_vol_mult = 1 << vol_shift;
+ else if(r == 1)
+ dig_vol_mult = 1 << vol_shift | 1 << (vol_shift - 2);
+ else
+ dig_vol_mult = 1 << vol_shift | 1 << (vol_shift - 1);
+ return dig_vol_mult;
+#else /* standard software volume */
/* Centibels -> fixedpoint */
return (uint32_t)fp_factor(fp_div(volume, 10, PCM_SW_VOLUME_FRACBITS),
PCM_SW_VOLUME_FRACBITS);
+#endif /* HAVE_SWVOL_32 */
}
+
/** Public functions **/
/* Produce final pcm scale factor */
diff --git a/firmware/powermgmt.c b/firmware/powermgmt.c
index a05e0aeb68..95763dc950 100644
--- a/firmware/powermgmt.c
+++ b/firmware/powermgmt.c
@@ -24,9 +24,7 @@
#include "kernel.h"
#include "thread.h"
#include "debug.h"
-#if !defined(DX50) && !defined(DX90)
#include "adc.h"
-#endif
#include "string.h"
#include "storage.h"
#include "power.h"
@@ -54,11 +52,7 @@
#include "pcf50606.h"
#endif
-/** Shared by sim **/
static int last_sent_battery_level = 100;
-/* battery level (0-100%) */
-int battery_percent = -1;
-void send_battery_level_event(void);
static void set_sleep_timer(int seconds);
static bool sleeptimer_active = false;
@@ -85,43 +79,12 @@ void handle_auto_poweroff(void);
static int poweroff_timeout = 0;
static long last_event_tick = 0;
-#if (CONFIG_BATTERY_MEASURE & PERCENTAGE_MEASURE) == PERCENTAGE_MEASURE
-#ifdef SIMULATOR
-int _battery_level(void) { return -1; }
-int _battery_voltage(void);
-extern const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11];
-extern const unsigned short percent_to_volt_charge[11];
+#if BATTERY_CAPACITY_INC > 0
+static int battery_capacity = BATTERY_CAPACITY_DEFAULT;
#else
-int _battery_voltage(void) { return -1; }
-const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11];
-const unsigned short percent_to_volt_charge[11];
-#endif
-#elif (CONFIG_BATTERY_MEASURE & VOLTAGE_MEASURE) == VOLTAGE_MEASURE
-int _battery_level(void) { return -1; }
-/*
- * Average battery voltage and charger voltage, filtered via a digital
- * exponential filter (aka. exponential moving average, scaled):
- * avgbat = y[n] = (N-1)/N*y[n-1] + x[n]. battery_millivolts = y[n] / N.
- */
-static unsigned int avgbat;
-/* filtered battery voltage, millivolts */
-static unsigned int battery_millivolts;
-#elif (CONFIG_BATTERY_MEASURE == 0)
-int _battery_voltage(void) { return -1; }
-int _battery_level(void) { return -1; }
-
-const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11];
-const unsigned short percent_to_volt_charge[11];
+# define battery_capacity BATTERY_CAPACITY_DEFAULT
#endif
-#if !(CONFIG_BATTERY_MEASURE & TIME_MEASURE)
-static int powermgmt_est_runningtime_min;
-int _battery_time(void) { return powermgmt_est_runningtime_min; }
-#endif
-
-/* default value, mAh */
-static int battery_capacity = BATTERY_CAPACITY_DEFAULT;
-
#if BATTERY_TYPES_COUNT > 1
static int battery_type = 0;
#else
@@ -133,109 +96,215 @@ unsigned short power_history[POWER_HISTORY_LEN] = {0};
#if (CONFIG_CPU == JZ4732) || (CONFIG_CPU == JZ4760B) || \
(CONFIG_CPU == X1000) || (CONFIG_PLATFORM & PLATFORM_HOSTED)
-static char power_stack[DEFAULT_STACK_SIZE + POWERMGMT_DEBUG_STACK];
+static char power_stack[DEFAULT_STACK_SIZE];
#else
-static char power_stack[DEFAULT_STACK_SIZE/2 + POWERMGMT_DEBUG_STACK];
+static char power_stack[DEFAULT_STACK_SIZE/2];
#endif
static const char power_thread_name[] = "power";
+/* Time estimation requires 64 bit math so don't use it in the bootloader.
+ * Also we need to be able to measure current, and not have a better time
+ * estimate source available. */
+#define HAVE_TIME_ESTIMATION \
+ (!defined(BOOTLOADER) && !(CONFIG_BATTERY_MEASURE & TIME_MEASURE) && \
+ (defined(CURRENT_NORMAL) || (CONFIG_BATTERY_MEASURE & CURRENT_MEASURE)))
-static int voltage_to_battery_level(int battery_millivolts);
-static void battery_status_update(void);
+#if !(CONFIG_BATTERY_MEASURE & PERCENTAGE_MEASURE)
+int _battery_level(void) { return -1; }
+#endif
+static int percent_now; /* Cached to avoid polling too often */
-#ifdef CURRENT_NORMAL /*only used if we have run current*/
-static int runcurrent(void);
+#if !(CONFIG_BATTERY_MEASURE & TIME_MEASURE)
+int _battery_time(void) { return -1; }
+#else
+static int time_now; /* Cached to avoid polling too often */
#endif
-void battery_read_info(int *voltage, int *level)
-{
- int millivolts = _battery_voltage();
- int percent;
+#if HAVE_TIME_ESTIMATION
+static int time_now; /* reported time in minutes */
+static int64_t time_cnt; /* reported time in seconds */
+static int64_t time_err; /* error... it's complicated */
+#endif
- if (voltage)
- *voltage = millivolts;
+#if !(CONFIG_BATTERY_MEASURE & VOLTAGE_MEASURE)
+int _battery_voltage(void) { return -1; }
+#else
+/* Data for the digital exponential filter */
+static int voltage_avg, voltage_now;
+#endif
- if (level) {
- percent = voltage_to_battery_level(millivolts);
- if (percent < 0)
- percent = _battery_level();
- *level = percent;
- }
-}
+#if !(CONFIG_BATTERY_MEASURE & CURRENT_MEASURE)
+int _battery_current(void) { return -1; }
+#else
+static int current_avg, current_now;
+#endif
-#if BATTERY_TYPES_COUNT > 1
-void set_battery_type(int type)
+/* The battery level can be obtained in two ways. If the target reports
+ * voltage, the battery level can be estminated using percent_to_volt_*
+ * curves. If the target can report the percentage directly, then that
+ * will be used instead of voltage-based estimation. */
+int battery_level(void)
{
- if (type != battery_type) {
- if ((unsigned)type >= BATTERY_TYPES_COUNT)
- type = 0;
+#ifdef HAVE_BATTERY_SWITCH
+ if ((power_input_status() & POWER_INPUT_BATTERY) == 0)
+ return -1;
+#endif
- battery_type = type;
- battery_status_update(); /* recalculate the battery status */
- }
+ return percent_now;
}
-#endif
-#ifdef BATTERY_CAPACITY_MIN
-void set_battery_capacity(int capacity)
+/* The time remaining to full charge/discharge can be provided by the
+ * target if it has an accurate way of doing this. Otherwise, if the
+ * target defines a valid battery capacity and can report the charging
+ * and discharging current, the time remaining will be estimated based
+ * on the battery level and the actual current usage. */
+int battery_time(void)
{
- if (capacity > BATTERY_CAPACITY_MAX)
- capacity = BATTERY_CAPACITY_MAX;
- if (capacity < BATTERY_CAPACITY_MIN)
- capacity = BATTERY_CAPACITY_MIN;
-
- battery_capacity = capacity;
-
- battery_status_update(); /* recalculate the battery status */
-}
+#if (CONFIG_BATTERY_MEASURE & TIME_MEASURE) || HAVE_TIME_ESTIMATION
+ return time_now;
+#else
+ return -1;
#endif
+}
-int get_battery_capacity(void)
+/* Battery voltage should always be reported if available, but it is
+ * optional if the the target reports battery percentage directly. */
+int battery_voltage(void)
{
- return battery_capacity;
+#if CONFIG_BATTERY_MEASURE & VOLTAGE_MEASURE
+ return voltage_now;
+#else
+ return -1;
+#endif
}
-int battery_time(void)
+/* Battery current can be estimated if the target defines CURRENT_NORMAL
+ * as the number of milliamps usually consumed by the device in a normal
+ * state. The target can also define other CURRENT_* values to estimate
+ * the power consumed by the backlight, remote display, SPDIF, etc. */
+int battery_current(void)
{
-#if ((CONFIG_BATTERY_MEASURE & TIME_MEASURE) == 0)
+#if CONFIG_BATTERY_MEASURE & CURRENT_MEASURE
+ return current_now;
+#elif defined(CURRENT_NORMAL)
+ int current = CURRENT_NORMAL;
-#ifndef CURRENT_NORMAL /* no estimation without current */
- return -1;
+#ifndef BOOTLOADER
+#if defined(HAVE_BACKLIGHT) && defined(CURRENT_BACKLIGHT)
+ if (backlight_get_current_timeout() == 0) /* LED always on */
+ current += CURRENT_BACKLIGHT;
+#endif
+
+#if defined(HAVE_RECORDING) && defined(CURRENT_RECORD)
+ if (audio_status() & AUDIO_STATUS_RECORD)
+ current += CURRENT_RECORD;
+#endif
+
+#if defined(HAVE_SPDIF_POWER) && defined(CURRENT_SPDIF_OUT)
+ if (spdif_powered())
+ current += CURRENT_SPDIF_OUT;
#endif
- if (battery_capacity <= 0) /* nor without capacity */
- return -1;
+#if defined(HAVE_REMOTE_LCD) && defined(CURRENT_REMOTE)
+ if (remote_detect())
+ current += CURRENT_REMOTE;
+#endif
+
+#if defined(HAVE_ATA_POWER_OFF) && defined(CURRENT_ATA)
+ if (ide_powered())
+ current += CURRENT_ATA;
+#endif
+
+#if CONFIG_CHARGING >= CHARGING_MONITOR
+ /* While charging we must report the charging current. */
+ if (charging_state()) {
+ current = CURRENT_MAX_CHG - current;
+ current = MIN(current, 1);
+ }
+#endif
+
+#endif /* BOOTLOADER */
+
+ return current;
+#else
+ return -1;
#endif
- return _battery_time();
}
-/* Returns battery level in percent */
-int battery_level(void)
+/* Initialize the battery voltage/current filters. This is called
+ * once by the power thread before entering the main polling loop. */
+static void average_init(void)
{
-#ifdef HAVE_BATTERY_SWITCH
- if ((power_input_status() & POWER_INPUT_BATTERY) == 0)
- return -1;
+#if CONFIG_BATTERY_MEASURE & VOLTAGE_MEASURE
+ voltage_now = _battery_voltage() + 15;
+
+ /* The battery voltage is usually a little lower directly after
+ turning on, because the disk was used heavily. Raise it by 5% */
+#ifdef HAVE_DISK_STORAGE
+#if CONFIG_CHARGING
+ if(!charger_inserted())
+#endif
+ {
+ voltage_now += (percent_to_volt_discharge[battery_type][6] -
+ percent_to_volt_discharge[battery_type][5]) / 2;
+ }
+#endif /* HAVE_DISK_STORAGE */
+
+ voltage_avg = voltage_now * BATT_AVE_SAMPLES;
+#endif /* CONFIG_BATTERY_MEASURE & VOLTAGE_MEASURE */
+
+#if CONFIG_BATTERY_MEASURE & CURRENT_MEASURE
+ current_now = _battery_current();
+ current_avg = current_now * BATT_CURRENT_AVE_SAMPLES;
#endif
- return battery_percent;
}
-/* Tells if the battery level is safe for disk writes */
-bool battery_level_safe(void)
+/* Sample the battery voltage/current and update the filters.
+ * Updated once every POWER_THREAD_STEP_TICKS. */
+static void average_step(bool low_battery)
{
-#if defined(NO_LOW_BATTERY_SHUTDOWN)
- return true;
-#elif (CONFIG_BATTERY_MEASURE & PERCENTAGE_MEASURE)
- return (battery_percent > 0);
-#elif defined(HAVE_BATTERY_SWITCH)
- /* Cannot rely upon the battery reading to be valid and the
- * device could be powered externally. */
- return input_millivolts() > battery_level_dangerous[battery_type];
+#if CONFIG_BATTERY_MEASURE & VOLTAGE_MEASURE
+ int millivolts = _battery_voltage();
+ if(low_battery) {
+ voltage_now = (millivolts + voltage_now + 1) / 2;
+ voltage_avg += voltage_now - voltage_avg / BATT_AVE_SAMPLES;
+ } else {
+ voltage_avg += millivolts - voltage_avg / BATT_AVE_SAMPLES;
+ voltage_now = voltage_avg / BATT_AVE_SAMPLES;
+ }
#else
- return battery_millivolts > battery_level_dangerous[battery_type];
+ (void)low_battery;
#endif
+
+#if CONFIG_BATTERY_MEASURE & CURRENT_MEASURE
+ current_avg += _battery_current() - current_avg / BATT_CURRENT_AVE_SAMPLES;
+ current_now = current_avg / BATT_CURRENT_AVE_SAMPLES;
+#endif
+}
+
+/* Send system battery level update events on reaching certain significant
+ * levels. This is called by battery_status_update() and does not have to
+ * be called separately. */
+static void send_battery_level_event(int percent)
+{
+ static const int levels[] = { 5, 15, 30, 50, 0 };
+ const int *level = levels;
+
+ while (*level)
+ {
+ if (percent <= *level && last_sent_battery_level > *level) {
+ last_sent_battery_level = *level;
+ queue_broadcast(SYS_BATTERY_UPDATE, last_sent_battery_level);
+ break;
+ }
+
+ level++;
+ }
}
-/* look into the percent_to_volt_* table and get a realistic battery level */
+#if !(CONFIG_BATTERY_MEASURE & PERCENTAGE_MEASURE) && \
+ (CONFIG_BATTERY_MEASURE & VOLTAGE_MEASURE)
+/* Look into the percent_to_volt_* table and estimate the battery level. */
static int voltage_to_percent(int voltage, const short* table)
{
if (voltage <= table[0]) {
@@ -257,20 +326,19 @@ static int voltage_to_percent(int voltage, const short* table)
}
}
-/* update battery level and estimated runtime, called once per minute or
- * when battery capacity / type settings are changed */
-static int voltage_to_battery_level(int battery_millivolts)
+/* Convert voltage to a battery level percentage using the appropriate
+ * percent_to_volt_* lookup table. */
+static int voltage_to_battery_level(int millivolts)
{
int level;
- if (battery_millivolts < 0)
+ if (millivolts < 0)
return -1;
#if CONFIG_CHARGING >= CHARGING_MONITOR
if (charging_state()) {
/* battery level is defined to be < 100% until charging is finished */
- level = voltage_to_percent(battery_millivolts,
- percent_to_volt_charge);
+ level = voltage_to_percent(millivolts, percent_to_volt_charge);
if (level > 99)
level = 99;
}
@@ -278,102 +346,147 @@ static int voltage_to_battery_level(int battery_millivolts)
#endif /* CONFIG_CHARGING >= CHARGING_MONITOR */
{
/* DISCHARGING or error state */
- level = voltage_to_percent(battery_millivolts,
- percent_to_volt_discharge[battery_type]);
+ level = voltage_to_percent(millivolts, percent_to_volt_discharge[battery_type]);
}
return level;
}
+#endif
+/* Update battery percentage and time remaining information.
+ *
+ * This will be called by the power thread after polling new battery data.
+ * It must also be called if the battery type or capacity changes.
+ */
static void battery_status_update(void)
{
- int millivolt = battery_voltage();
+#if CONFIG_BATTERY_MEASURE & PERCENTAGE_MEASURE
int level = _battery_level();
+#elif CONFIG_BATTERY_MEASURE & VOLTAGE_MEASURE
+ int level = voltage_to_battery_level(voltage_now);
+#else
+ /* This should be a compile time error? */
+ int level = -1;
+#endif
- if (level < 0)
- level = voltage_to_battery_level(millivolt);
+#if CONFIG_BATTERY_MEASURE & TIME_MEASURE
+ time_now = _battery_time();
+#elif HAVE_TIME_ESTIMATION
+ /* TODO: This is essentially a bad version of coloumb counting,
+ * so in theory using coloumb counters when they are available
+ * should provide a more accurate result. Also note that this
+ * is hard-coded with a HZ/2 update rate to simplify arithmetic. */
-#ifdef CURRENT_NORMAL /*don't try to estimate run or charge
- time without normal current defined*/
- /* calculate estimated remaining running time */
+ int current = battery_current();
+ int resolution = battery_capacity * 36;
+
+ int time_est = 0;
+ if(level >= 0 && current > 0) {
#if CONFIG_CHARGING >= CHARGING_MONITOR
- if (charging_state()) {
- /* charging: remaining charging time */
- powermgmt_est_runningtime_min = (100 - level)*battery_capacity*60
- / 100 / (CURRENT_MAX_CHG - runcurrent());
- }
- else
+ if (charging_state())
+ time_est = (100 - level) * battery_capacity * 36 / current;
+ else
#endif
+ time_est = level * battery_capacity * 36 / current;
+
+ /* The first term nudges the counter toward the estimate. */
+ time_err += current * (time_est - time_cnt);
+ }
+
+ /* The second term decrements the counter due to elapsed time. */
+ time_err -= resolution;
- /* discharging: remaining running time */
- if (level > 0 && (millivolt > percent_to_volt_discharge[battery_type][0]
- || millivolt < 0)) {
- /* linear extrapolation */
- powermgmt_est_runningtime_min = (level + battery_percent)*60
- * battery_capacity / 200 / runcurrent();
+ /* Arbitrary cutoff to ensure we don't get too far out
+ * of sync. Seems to work well on synthetic tests. */
+ if(time_err > resolution * 12 ||
+ time_err < -resolution * 13) {
+ time_cnt = time_est;
+ time_err = 0;
}
- if (0 > powermgmt_est_runningtime_min) {
- powermgmt_est_runningtime_min = 0;
+
+ if(resolution > 0) {
+ /* Convert the error into a time and adjust the counter. */
+ int64_t adjustment = time_err / (2 * resolution);
+ time_cnt += adjustment;
+ time_err -= adjustment * (2 * resolution);
}
+
+ /* Update the reported time based on the counter. */
+ time_now = (time_cnt + 30) / 60;
+ if(time_now < 0)
+ time_now = 0;
#endif
- battery_percent = level;
- send_battery_level_event();
+ percent_now = level;
+ send_battery_level_event(level);
}
-#ifdef CURRENT_NORMAL /*check that we have a current defined in a config file*/
-
-/*
- * Estimate how much current we are drawing just to run.
- */
-static int runcurrent(void)
+void battery_read_info(int *voltage, int *level)
{
- int current = CURRENT_NORMAL;
+ int millivolts = _battery_voltage();
-#ifndef BOOTLOADER
- if (usb_inserted()
-#ifdef HAVE_USB_POWER
- #if (CURRENT_USB < CURRENT_NORMAL)
- || usb_powered_only()
- #else
- && !usb_powered_only()
- #endif
-#endif
- ) {
- current = CURRENT_USB;
- }
+ if (voltage)
+ *voltage = millivolts;
-#if defined(HAVE_BACKLIGHT)
- if (backlight_get_current_timeout() == 0) /* LED always on */
- current += CURRENT_BACKLIGHT;
+ if (level) {
+#if (CONFIG_BATTERY_MEASURE & PERCENTAGE_MEASURE)
+ *level = _battery_level();
+#elif (CONFIG_BATTERY_MEASURE & VOLTAGE_MEASURE)
+ *level = voltage_to_battery_level(millivolts);
+#else
+ *level = -1;
#endif
+ }
+}
-#if defined(HAVE_RECORDING) && defined(CURRENT_RECORD)
- if (audio_status() & AUDIO_STATUS_RECORD)
- current += CURRENT_RECORD;
-#endif
+#if BATTERY_TYPES_COUNT > 1
+void set_battery_type(int type)
+{
+ if(type < 0 || type > BATTERY_TYPES_COUNT)
+ type = 0;
-#ifdef HAVE_SPDIF_POWER
- if (spdif_powered())
- current += CURRENT_SPDIF_OUT;
+ if (type != battery_type) {
+ battery_type = type;
+ battery_status_update(); /* recalculate the battery status */
+ }
+}
#endif
-#ifdef HAVE_REMOTE_LCD
- if (remote_detect())
- current += CURRENT_REMOTE;
-#endif
+#if BATTERY_CAPACITY_INC > 0
+void set_battery_capacity(int capacity)
+{
+ if (capacity > BATTERY_CAPACITY_MAX)
+ capacity = BATTERY_CAPACITY_MAX;
+ if (capacity < BATTERY_CAPACITY_MIN)
+ capacity = BATTERY_CAPACITY_MIN;
-#if defined(HAVE_ATA_POWER_OFF) && defined(CURRENT_ATA)
- if (ide_powered())
- current += CURRENT_ATA;
+ if (capacity != battery_capacity) {
+ battery_capacity = capacity;
+ battery_status_update(); /* recalculate the battery status */
+ }
+}
#endif
-#endif /* BOOTLOADER */
-
- return current;
+int get_battery_capacity(void)
+{
+ return battery_capacity;
}
-#endif /* CURRENT_NORMAL */
+/* Tells if the battery level is safe for disk writes */
+bool battery_level_safe(void)
+{
+#if defined(NO_LOW_BATTERY_SHUTDOWN)
+ return true;
+#elif CONFIG_BATTERY_MEASURE & PERCENTAGE_MEASURE
+ return percent_now > 0;
+#elif defined(HAVE_BATTERY_SWITCH)
+ /* Cannot rely upon the battery reading to be valid and the
+ * device could be powered externally. */
+ return input_millivolts() > battery_level_dangerous[battery_type];
+#else
+ return voltage_now > battery_level_dangerous[battery_type];
+#endif
+}
/* Check to see whether or not we've received an alarm in the last second */
#ifdef HAVE_RTC_ALARM
@@ -387,16 +500,22 @@ static void power_thread_rtc_process(void)
/* switch off unit if battery level is too low for reliable operation */
bool query_force_shutdown(void)
{
+#if CONFIG_CHARGING
+ /* It doesn't make sense to force shutdown when externally powered. */
+ if (power_input_present())
+ return false;
+#endif
+
#if defined(NO_LOW_BATTERY_SHUTDOWN)
return false;
#elif CONFIG_BATTERY_MEASURE & PERCENTAGE_MEASURE
- return battery_percent == 0;
+ return percent_now == 0;
#elif defined(HAVE_BATTERY_SWITCH)
/* Cannot rely upon the battery reading to be valid and the
* device could be powered externally. */
return input_millivolts() < battery_level_shutoff[battery_type];
#else
- return battery_millivolts < battery_level_shutoff[battery_type];
+ return voltage_now < battery_level_shutoff[battery_type];
#endif
}
@@ -407,8 +526,13 @@ bool query_force_shutdown(void)
*/
void reset_battery_filter(int millivolts)
{
- avgbat = millivolts * BATT_AVE_SAMPLES;
- battery_millivolts = millivolts;
+ voltage_avg = millivolts * BATT_AVE_SAMPLES;
+ voltage_now = millivolts;
+#if CONFIG_BATTERY_MEASURE & CURRENT_MEASURE
+ /* current would probably be inaccurate too */
+ current_now = _battery_current();
+ current_avg = current_now * BATT_CURRENT_AVE_SAMPLES;
+#endif
battery_status_update();
}
#endif /* HAVE_BATTERY_SWITCH */
@@ -543,90 +667,15 @@ static inline bool detect_charger(unsigned int pwr)
}
#endif /* CONFIG_CHARGING */
-
#if CONFIG_BATTERY_MEASURE & VOLTAGE_MEASURE
-/* Returns filtered battery voltage [millivolts] */
-int battery_voltage(void)
-{
- return battery_millivolts;
-}
-
-static void average_init(void)
-{
- /* initialize the voltages for the exponential filter */
- avgbat = _battery_voltage() + 15;
-
-#ifdef HAVE_DISK_STORAGE /* this adjustment is only needed for HD based */
- /* The battery voltage is usually a little lower directly after
- turning on, because the disk was used heavily. Raise it by 5% */
-#if CONFIG_CHARGING
- if (!charger_inserted()) /* only if charger not connected */
-#endif
- {
- avgbat += (percent_to_volt_discharge[battery_type][6] -
- percent_to_volt_discharge[battery_type][5]) / 2;
- }
-#endif /* HAVE_DISK_STORAGE */
-
- avgbat = avgbat * BATT_AVE_SAMPLES;
- battery_millivolts = power_history[0] = avgbat / BATT_AVE_SAMPLES;
-}
-
-static void average_step(void)
-{
- avgbat += _battery_voltage() - avgbat / BATT_AVE_SAMPLES;
- /*
- * battery_millivolts is the millivolt-scaled filtered battery value.
- */
- battery_millivolts = avgbat / BATT_AVE_SAMPLES;
-}
-
-static void average_step_low(void)
-{
- battery_millivolts = (_battery_voltage() + battery_millivolts + 1) / 2;
- avgbat += battery_millivolts - avgbat / BATT_AVE_SAMPLES;
-}
-
-static void init_battery_percent(void)
-{
-#if CONFIG_CHARGING
- if (charger_inserted()) {
- battery_percent = voltage_to_percent(battery_millivolts,
- percent_to_volt_charge);
- }
- else
-#endif
- {
- battery_percent = voltage_to_percent(battery_millivolts,
- percent_to_volt_discharge[battery_type]);
- battery_percent += battery_percent < 100;
- }
-
-}
-
static int power_hist_item(void)
{
- return battery_millivolts;
+ return voltage_now;
}
-#define power_history_unit() battery_millivolts
-
#else
-int battery_voltage(void)
-{
- return -1;
-}
-
-static void average_init(void) {}
-static void average_step(void) {}
-static void average_step_low(void) {}
-static void init_battery_percent(void)
-{
- battery_percent = _battery_level();
-}
-
static int power_hist_item(void)
{
- return battery_percent;
+ return percent_now;
}
#endif
@@ -667,13 +716,11 @@ static inline void power_thread_step(void)
|| charger_input_state == CHARGER
#endif
) {
- average_step();
- /* update battery status every time an update is available */
+ average_step(false);
battery_status_update();
}
- else if (battery_percent < 8) {
- average_step_low();
- /* update battery status every time an update is available */
+ else if (percent_now < 8) {
+ average_step(true);
battery_status_update();
/*
@@ -694,10 +741,18 @@ static void power_thread(void)
/* Delay reading the first battery level */
#ifdef MROBE_100
while (_battery_voltage() > 4200) /* gives false readings initially */
+ {
#elif defined(DX50) || defined(DX90)
while (_battery_voltage() < 1) /* can give false readings initially */
-#endif
{
+#elif defined(EROS_QN) || defined(FIIO_M3K) || defined(SHANLING_Q1)
+
+ /* wait until the first battery read is ready */
+ while (_battery_voltage() <= 0)
+ {
+#else
+ {
+#endif
sleep(HZ/100);
}
@@ -711,7 +766,7 @@ static void power_thread(void)
/* initialize voltage averaging (if available) */
average_init();
/* get initial battery level value (in %) */
- init_battery_percent();
+ battery_status_update();
/* get some initial data for the power curve */
collect_power_history();
@@ -762,7 +817,7 @@ void powermgmt_init(void)
}
/* Various hardware housekeeping tasks relating to shutting down the player */
-void shutdown_hw(void)
+void shutdown_hw(enum shutdown_type sd_type)
{
charging_algorithm_close();
audio_stop();
@@ -802,7 +857,17 @@ void shutdown_hw(void)
eeprom chips are quite slow and might be still writing the last
byte. */
sleep(HZ/4);
- power_off();
+
+ switch (sd_type) {
+ case SHUTDOWN_POWER_OFF:
+ default:
+ power_off();
+ break;
+
+ case SHUTDOWN_REBOOT:
+ system_reboot();
+ break;
+ }
}
void set_poweroff_timeout(int timeout)
@@ -817,10 +882,9 @@ void reset_poweroff_timer(void)
set_sleep_timer(sleeptimer_duration);
}
-void sys_poweroff(void)
-{
#ifndef BOOTLOADER
- logf("sys_poweroff()");
+static void sys_shutdown_common(void)
+{
/* If the main thread fails to shut down the system, we will force a
power off after an 20 second timeout - 28 seconds if recording */
if (shutdown_timeout == 0) {
@@ -838,9 +902,26 @@ void sys_poweroff(void)
shutdown_timeout += HZ*20;
#endif
}
+}
+#endif /* BOOTLOADER */
+void sys_poweroff(void)
+{
+#ifndef BOOTLOADER
+ logf("sys_poweroff()");
+ sys_shutdown_common();
queue_broadcast(SYS_POWEROFF, 0);
-#endif /* BOOTLOADER */
+#endif
+}
+
+/* not to be confused with system_reboot... :( */
+void sys_reboot(void)
+{
+#ifndef BOOTLOADER
+ logf("sys_reboot()");
+ sys_shutdown_common();
+ queue_broadcast(SYS_REBOOT, 0);
+#endif
}
void cancel_shutdown(void)
@@ -856,25 +937,6 @@ void cancel_shutdown(void)
shutdown_timeout = 0;
}
-/* Send system battery level update events on reaching certain significant
- levels. This must be called after battery_percent has been updated. */
-void send_battery_level_event(void)
-{
- static const int levels[] = { 5, 15, 30, 50, 0 };
- const int *level = levels;
-
- while (*level)
- {
- if (battery_percent <= *level && last_sent_battery_level > *level) {
- last_sent_battery_level = *level;
- queue_broadcast(SYS_BATTERY_UPDATE, last_sent_battery_level);
- break;
- }
-
- level++;
- }
-}
-
void set_sleeptimer_duration(int minutes)
{
set_sleep_timer(minutes * 60);
@@ -893,6 +955,11 @@ static void set_sleep_timer(int seconds)
sleeptimer_duration = seconds;
}
+bool get_sleep_timer_active(void)
+{
+ return sleeptimer_active;
+}
+
int get_sleep_timer(void)
{
if (sleeptimer_active && (sleeptimer_endtick >= current_tick))
diff --git a/firmware/rolo.c b/firmware/rolo.c
index 622110576a..a3e6d5c2b9 100644
--- a/firmware/rolo.c
+++ b/firmware/rolo.c
@@ -40,33 +40,29 @@
#include "loader_strerror.h"
#if defined(MI4_FORMAT)
-#include "crc32-mi4.h"
#include "mi4-loader.h"
-#if defined(HAVE_BOOTDATA) && !defined(SIMULATOR)
-#include "bootdata.h"
-#include "crc32.h"
-extern int write_bootdata(unsigned char* buf, int len, unsigned int boot_volume); /*mi4-loader.c*/
-#endif
#define LOAD_FIRMWARE(a,b,c) load_mi4(a,b,c)
#elif defined(RKW_FORMAT)
#include "rkw-loader.h"
#define LOAD_FIRMWARE(a,b,c) load_rkw(a,b,c)
#else
#include "rb-loader.h"
+#define LOAD_FIRMWARE(a,b,c) load_firmware(a,b,c)
+#endif
+
#if defined(HAVE_BOOTDATA) && !defined(SIMULATOR)
+#include "multiboot.h"
#include "bootdata.h"
#include "crc32.h"
-extern int write_bootdata(unsigned char* buf, int len, unsigned int boot_volume); /*rb-loader.c*/
-#endif
-#define LOAD_FIRMWARE(a,b,c) load_firmware(a,b,c)
#endif
#if CONFIG_CPU == AS3525v2
#include "ascodec.h"
#endif
-#if !defined(IRIVER_IFP7XX_SERIES)
-/* FIX: this doesn't work on iFP */
+#if defined(FIIO_M3K)
+#include "backlight-target.h"
+#endif
#define IRQ0_EDGE_TRIGGER 0x80
@@ -133,9 +129,10 @@ static void rolo_error(const char *text)
lcd_scroll_stop();
}
-#if CONFIG_CPU == IMX31L || CONFIG_CPU == RK27XX
+#if CONFIG_CPU == IMX31L || CONFIG_CPU == RK27XX || CONFIG_CPU == X1000
/* this is in firmware/target/arm/imx31/rolo_restart.c for IMX31 */
/* this is in firmware/target/arm/rk27xx/rolo_restart.c for rk27xx */
+/* this is in firmware/target/mips/ingenic_x1000/boot-x1000.c for X1000 */
extern void rolo_restart(const unsigned char* source, unsigned char* dest,
int length);
#else
@@ -242,7 +239,7 @@ int rolo_load(const char* filename)
/* get the system buffer. release only in case of error, otherwise
* we don't return anyway */
- rolo_handle = core_alloc_maximum("rolo", &filebuf_size, NULL);
+ rolo_handle = core_alloc_maximum(&filebuf_size, &buflib_ops_locked);
if (rolo_handle < 0)
{
rolo_error("OOM");
@@ -292,6 +289,12 @@ int rolo_load(const char* filename)
lcd_remote_puts(0, 1, "Executing");
lcd_remote_update();
#endif
+
+#if defined(FIIO_M3K)
+ /* Avoids the LCD backlight ramping down & up weirdly */
+ backlight_hw_off();
+#endif
+
adc_close();
#if CONFIG_CPU == AS3525v2
/* Set CVDD1 power supply to default*/
@@ -321,13 +324,3 @@ int rolo_load(const char* filename)
/* never reached */
return 0;
}
-
-#else /* !defined(IRIVER_IFP7XX_SERIES) */
-int rolo_load(const char* filename)
-{
- /* dummy */
- (void)filename;
- return 0;
-}
-
-#endif /* !defined(IRIVER_IFP7XX_SERIES) */
diff --git a/firmware/screendump.c b/firmware/screendump.c
index 1acaaafba6..7d09b0cd5e 100644
--- a/firmware/screendump.c
+++ b/firmware/screendump.c
@@ -102,7 +102,7 @@ static void (*screen_dump_hook)(int fh) = NULL;
void screen_dump(void)
{
int fd, y;
- char filename[32];
+ char filename[MAX_PATH];
fb_data *src;
#if LCD_DEPTH == 1
@@ -306,7 +306,7 @@ static const unsigned char rbmpheader[] =
void remote_screen_dump(void)
{
int fd, y;
- char filename[32];
+ char filename[MAX_PATH];
fb_remote_data *src;
#if LCD_REMOTE_DEPTH == 1
diff --git a/firmware/scroll_engine.c b/firmware/scroll_engine.c
index 91f9d1f868..c32f4632e2 100644
--- a/firmware/scroll_engine.c
+++ b/firmware/scroll_engine.c
@@ -37,28 +37,15 @@
#endif
#include "scroll_engine.h"
-
-/* private helper function for the scroll engine. Do not use in apps/.
- * defined in lcd-bitmap-common.c */
-extern struct viewport *lcd_get_viewport(bool *is_defaut);
-#ifdef HAVE_REMOTE_LCD
-extern struct viewport *lcd_remote_get_viewport(bool *is_defaut);
-#endif
-
static const char scroll_tick_table[18] = {
/* Hz values [f(x)=100.8/(x+.048)]:
1, 1.25, 1.55, 2, 2.5, 3.12, 4, 5, 6.25, 8.33, 10, 12.5, 16.7, 20, 25, 33, 49.2, 96.2 */
100, 80, 64, 50, 40, 32, 25, 20, 16, 12, 10, 8, 6, 5, 4, 3, 2, 1
};
-static void scroll_thread(void);
-static const char scroll_name[] = "scroll";
-
#include "drivers/lcd-scroll.c"
#ifdef HAVE_REMOTE_LCD
-static struct event_queue scroll_queue SHAREDBSS_ATTR;
-
/* copied from lcd-remote-1bit.c */
/* Compile 1 bit vertical packing LCD driver for remote LCD */
#undef LCDFN
@@ -67,6 +54,10 @@ static struct event_queue scroll_queue SHAREDBSS_ATTR;
#define LCDM(ma) LCD_REMOTE_ ## ma
#include "drivers/lcd-scroll.c"
+#endif /* HAVE_REMOTE_LCD */
+
+#if defined(HAVE_REMOTE_LCD) && !defined(BOOTLOADER)
+static struct event_queue scroll_queue SHAREDBSS_ATTR;
static void sync_display_ticks(void)
{
@@ -109,11 +100,14 @@ static bool scroll_process_message(int delay)
return false;
}
-#endif /* HAVE_REMOTE_LCD */
+#endif /* HAVE_REMOTE_LCD && !BOOTLOADER */
+#if !defined(BOOTLOADER)
+static void scroll_thread(void);
+static const char scroll_name[] = "scroll";
static void scroll_thread(void) NORETURN_ATTR;
-#ifdef HAVE_REMOTE_LCD
+#ifdef HAVE_REMOTE_LCD
static void scroll_thread(void)
{
enum
@@ -178,24 +172,20 @@ static void scroll_thread(void)
}
}
}
-#else
+#else /* !HAVE_REMOTE_LCD */
static void scroll_thread(void)
{
while (1)
{
sleep(lcd_scroll_info.ticks);
-#if !defined(BOOTLOADER)
#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
if (lcd_active())
#endif
lcd_scroll_worker();
-#endif /* !BOOTLOADER */
}
}
-#endif /* HAVE_REMOTE_LCD */
-
+#endif /* !HAVE_REMOTE_LCD */
-#ifndef BOOTLOADER
void scroll_init(void)
{
static char scroll_stack[DEFAULT_STACK_SIZE*3];
@@ -207,9 +197,9 @@ void scroll_init(void)
IF_PRIO(, PRIORITY_USER_INTERFACE)
IF_COP(, CPU));
}
-#else
+#else /* BOOTLOADER */
void scroll_init(void)
{
/* DUMMY */
}
-#endif /* ndef BOOTLOADER*/
+#endif /* BOOTLOADER*/
diff --git a/firmware/system.c b/firmware/system.c
index 537e901b05..e2fdff0e59 100644
--- a/firmware/system.c
+++ b/firmware/system.c
@@ -89,6 +89,7 @@ char * cpu_boost_log_getlog_next(void)
void cpu_boost_(bool on_off, char* location, int line)
{
+ int item = cpu_boost_calls_count;
if (!cpu_boost_lock())
return;
@@ -98,12 +99,13 @@ void cpu_boost_(bool on_off, char* location, int line)
cpu_boost_calls_count--;
if (cpu_boost_calls_count < 0)
cpu_boost_calls_count = 0;
+ item += cpu_boost_first;
}
if (cpu_boost_calls_count < MAX_BOOST_LOG)
{
int message = (cpu_boost_first+cpu_boost_calls_count)%MAX_BOOST_LOG;
- snprintf(cpu_boost_calls[message], MAX_PATH,
- "%c %s:%d",on_off?'B':'U',location,line);
+ snprintf(cpu_boost_calls[message], MAX_PATH,"%d.) %c %d %s:%d",
+ item,on_off?'B':'U',boost_counter,location,line);
cpu_boost_calls_count++;
}
#else
diff --git a/firmware/target/arm/as3525/audio-as3525.c b/firmware/target/arm/as3525/audio-as3525.c
index e4bb39b406..691ccaa028 100644
--- a/firmware/target/arm/as3525/audio-as3525.c
+++ b/firmware/target/arm/as3525/audio-as3525.c
@@ -67,6 +67,7 @@ void audio_input_mux(int source, unsigned flags)
{
default: /* playback - no recording */
source = AUDIO_SRC_PLAYBACK;
+ /*fallthrough*/
case AUDIO_SRC_PLAYBACK:
if (source != last_source)
{
diff --git a/firmware/target/arm/as3525/debug-as3525.c b/firmware/target/arm/as3525/debug-as3525.c
index c4c4a7f4dc..4848f0e5c5 100644
--- a/firmware/target/arm/as3525/debug-as3525.c
+++ b/firmware/target/arm/as3525/debug-as3525.c
@@ -288,14 +288,19 @@ static bool dbg_btn(bool *done, int *x)
{
case DEBUG_CANCEL:
*done = true;
+ /*fallthrough*/
case DEBUG_NEXT:
cont = false;
+ /*fallthrough*/
case DEBUG_LEFT_JUSTIFY:
(*x) = 0;
sleep(HZ/5);
break;
case DEBUG_LEFT_SCROLL:
(*x)--;
+ break;
+ default:
+ break;
}
}
lcd_clear_display();
@@ -361,7 +366,7 @@ bool dbg_hw_info(void)
calc_freq(CLK_IDE)/D_MHZ);
lcd_putsf(x, line++, "%s:%3dMHz %3dMHz", "DBOP", AS3525_DBOP_FREQ/D_MHZ,
calc_freq(CLK_DBOP)/D_MHZ);
- lcd_putsf(x, line++, "%s:%3dMHz %3dMHz", "I2C ", AS3525_I2C_FREQ/D_KHZ,
+ lcd_putsf(x, line++, "%s:%3dkHz %3dkHz", "I2C ", AS3525_I2C_FREQ/D_KHZ,
calc_freq(CLK_I2C)/D_KHZ);
lcd_putsf(x, line++, "I2SI: %s %3dMHz", (CGU_AUDIO & (1<<23)) ?
"on " : "off" , calc_freq(CLK_I2SI)/D_MHZ);
@@ -390,7 +395,7 @@ bool dbg_hw_info(void)
lcd_putsf(x, line++, "%s:%3dMHz %3dMHz", "SSP ", AS3525_SSP_FREQ/D_MHZ,
calc_freq(CLK_SSP)/D_MHZ);
else
- lcd_putsf(x, line++, "SSP :%3dMHz %3dKHz", AS3525_SSP_FREQ/D_MHZ,
+ lcd_putsf(x, line++, "SSP :%3dMHz %3dkHz", AS3525_SSP_FREQ/D_MHZ,
calc_freq(CLK_SSP)/D_KHZ);
#endif /* CONFIG_CPU == AS3525 */
lcd_putsf(x, line++, "USB : %3dMHz", calc_freq(CLK_USB)/D_MHZ);
@@ -610,4 +615,4 @@ int get_cpu_voltage_setting(void)
return value;
}
-#endif /* HAVE_ADJUSTABLE_CPU_VOLTAGE */ \ No newline at end of file
+#endif /* HAVE_ADJUSTABLE_CPU_VOLTAGE */
diff --git a/firmware/target/arm/as3525/fmradio-i2c-as3525.c b/firmware/target/arm/as3525/fmradio-i2c-as3525.c
index 5b629f5ad4..7f6cb8366b 100644
--- a/firmware/target/arm/as3525/fmradio-i2c-as3525.c
+++ b/firmware/target/arm/as3525/fmradio-i2c-as3525.c
@@ -185,6 +185,11 @@ int fmradio_i2c_read(unsigned char address, unsigned char* buf, int count)
}
#ifdef HAVE_RDS_CAP
+/* On the Sansa Clip Zip, the tuner interrupt line is routed to the SoC so we
+ * can use to detect when a RDS packet is ready. On the Clip+, we have to
+ * regularly poll. */
+
+#if !(CONFIG_RDS & RDS_CFG_POLL)
/* Low-level RDS Support */
static struct semaphore rds_sema;
static uint32_t rds_stack[DEFAULT_STACK_SIZE/sizeof(uint32_t)];
@@ -231,4 +236,5 @@ void si4700_rds_init(void)
create_thread(rds_thread, rds_stack, sizeof(rds_stack), 0, "rds"
IF_PRIO(, PRIORITY_REALTIME) IF_COP(, CPU));
}
+#endif /* !(CONFIG_RDS & RDS_CFG_POLL) */
#endif /* HAVE_RDS_CAP */
diff --git a/firmware/target/arm/as3525/lcd-ssd1303.c b/firmware/target/arm/as3525/lcd-ssd1303.c
index 2aa0b844c5..186fdcacbe 100644
--- a/firmware/target/arm/as3525/lcd-ssd1303.c
+++ b/firmware/target/arm/as3525/lcd-ssd1303.c
@@ -310,6 +310,7 @@ static void internal_update_rect(int x, int y, int width, int height)
const int column_high = get_column_high_byte(x);
const int column_low = get_column_low_byte(x);
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
/* Copy specified rectange bitmap to hardware */
for (; y <= height; y++)
{
@@ -320,7 +321,7 @@ static void internal_update_rect(int x, int y, int width, int height)
(column_low)
);
- lcd_write_data (FBADDR(x,y), width);
+ lcd_write_data (fbaddr(x,y), width);
}
lcd_write_command (LCD_NOP); /* return to command mode */
diff --git a/firmware/target/arm/as3525/pcm-as3525.c b/firmware/target/arm/as3525/pcm-as3525.c
index dfbda388be..e480140497 100644
--- a/firmware/target/arm/as3525/pcm-as3525.c
+++ b/firmware/target/arm/as3525/pcm-as3525.c
@@ -200,18 +200,6 @@ void pcm_dma_apply_settings(void)
0x01ffffff);
}
-const void * pcm_play_dma_get_peak_buffer(int *count)
-{
- int oldstatus = disable_irq_save();
- size_t addr = DMAC_CH_SRC_ADDR(0);
- size_t start_addr = (size_t)dma_start_addr;
- size_t start_size = dma_start_size;
- restore_interrupt(oldstatus);
-
- *count = (start_size - addr + start_addr) >> 2;
- return (void*)AS3525_UNCACHED_ADDR(addr);
-}
-
#ifdef HAVE_PCM_DMA_ADDRESS
void * pcm_dma_addr(void *addr)
{
diff --git a/firmware/target/arm/as3525/sansa-clipzip/lcd-clipzip.c b/firmware/target/arm/as3525/sansa-clipzip/lcd-clipzip.c
index 03ed1de5d5..48594a2ac9 100644
--- a/firmware/target/arm/as3525/sansa-clipzip/lcd-clipzip.c
+++ b/firmware/target/arm/as3525/sansa-clipzip/lcd-clipzip.c
@@ -440,9 +440,10 @@ void lcd_update_rect(int x, int y, int width, int height)
/* setup GRAM write window */
lcd_setup_rect(x, x_end - 1, y, y_end - 1);
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
/* write to GRAM */
for (row = y; row < y_end; row++) {
- lcd_write_data(FBADDR(x,row), width);
+ lcd_write_data(fbaddr(x,row), width);
}
}
diff --git a/firmware/target/arm/as3525/system-as3525.c b/firmware/target/arm/as3525/system-as3525.c
index 83ccb55f79..0450c8b410 100644
--- a/firmware/target/arm/as3525/system-as3525.c
+++ b/firmware/target/arm/as3525/system-as3525.c
@@ -180,7 +180,7 @@ void INT_GPIOA(void)
void button_gpioa_isr(void);
button_gpioa_isr();
#endif
-#ifdef HAVE_RDS_CAP
+#if defined(HAVE_RDS_CAP) && !(CONFIG_RDS & RDS_CFG_POLL)
void tuner_isr(void);
tuner_isr();
#endif
diff --git a/firmware/target/arm/as3525/usb-as3525.c b/firmware/target/arm/as3525/usb-as3525.c
index d798d4da83..3f636256a8 100644
--- a/firmware/target/arm/as3525/usb-as3525.c
+++ b/firmware/target/arm/as3525/usb-as3525.c
@@ -53,12 +53,21 @@ static int usb_status = USB_EXTRACTED;
void usb_enable(bool on)
{
#if defined(HAVE_USBSTACK)
+ static int boosted = 0;
if (on){
- cpu_boost(1);
+ if (boosted == 0)
+ {
+ cpu_boost(1);
+ boosted = 1;
+ }
usb_core_init();
} else {
usb_core_exit();
- cpu_boost(0);
+ if(boosted == 1)
+ {
+ cpu_boost(0);
+ boosted = 0;
+ }
}
#else
(void)on;
diff --git a/firmware/target/arm/as3525/usb-drv-as3525.c b/firmware/target/arm/as3525/usb-drv-as3525.c
index 01fae5f0ab..d0875ed48c 100644
--- a/firmware/target/arm/as3525/usb-drv-as3525.c
+++ b/firmware/target/arm/as3525/usb-drv-as3525.c
@@ -412,12 +412,12 @@ void usb_drv_cancel_all_transfers(void)
restore_irq(flags);
}
-int usb_drv_recv(int ep, void *ptr, int len)
+int usb_drv_recv_nonblocking(int ep, void *ptr, int len)
{
struct usb_dev_dma_desc *uc_desc = endpoints[ep][1].uc_desc;
ep &= 0x7f;
- logf("usb_drv_recv(%d,%x,%d)\n", ep, (int)ptr, len);
+ logf("usb_drv_recv_nonblocking(%d,%x,%d)\n", ep, (int)ptr, len);
if (len > USB_DMA_DESC_RXTX_BYTES)
panicf("usb_recv: len=%d > %d", len, USB_DMA_DESC_RXTX_BYTES);
@@ -655,7 +655,7 @@ static void handle_out_ep(int ep)
req->wIndex,
req->wLength);
- usb_core_control_request(&req_copy);
+ usb_core_legacy_control_request(&req_copy);
setup_desc_init(setup_desc);
ep_sts &= ~USB_EP_STAT_SETUP_RCVD;
@@ -674,7 +674,8 @@ static void handle_out_ep(int ep)
* The usb_storage buffer is 63KB, but Linux sends 120KB.
* We get the first part, but upon re-enabling receive dma we
* get a 'buffer not available' error from the hardware, since
- * we haven't gotten the next usb_drv_recv() from the stack yet.
+ * we haven't gotten the next usb_drv_recv_nonblocking() from
+ * the stack yet.
* It seems the NAK bit is ignored here and the HW tries to dma
* the incoming data anyway.
* In theory I think the BNA error should be recoverable, but
@@ -759,7 +760,7 @@ void INT_USB_FUNC(void)
got_set_configuration = 1;
set_config.wValue = USB_DEV_STS & USB_DEV_STS_MASK_CFG;
- usb_core_control_request(&set_config);
+ usb_core_legacy_control_request(&set_config);
intr &= ~USB_DEV_INTR_SET_CONFIG;
}
if (intr & USB_DEV_INTR_EARLY_SUSPEND) {/* idle >3ms detected */
diff --git a/firmware/target/arm/ata-as-arm.S b/firmware/target/arm/ata-as-arm.S
index 101bc4dcc1..16c2928bf1 100644
--- a/firmware/target/arm/ata-as-arm.S
+++ b/firmware/target/arm/ata-as-arm.S
@@ -139,9 +139,9 @@ copy_read_sectors:
.r_end2_u:
tst r1, #1 /* one halfword left? */
- ldrneh r4, [r2]
+ ldrhne r4, [r2]
orrne r3, r3, r4, lsl #8
- strneh r3, [r0], #2
+ strhne r3, [r0], #2
movne r3, r4, lsr #8
strb r3, [r0], #1 /* store final byte */
@@ -151,8 +151,8 @@ copy_read_sectors:
/* 16-bit aligned */
.r_aligned:
tst r0, #2 /* 32 bit aligned? */
- ldrneh r3, [r2] /* no: read first halfword */
- strneh r3, [r0], #2 /* store */
+ ldrhne r3, [r2] /* no: read first halfword */
+ strhne r3, [r0], #2 /* store */
subne r1, r1, #1 /* one halfword taken */
sub r1, r1, #8 /* adjust for zero-check and doing 8 halfwords/loop */
@@ -186,14 +186,14 @@ copy_read_sectors:
.r_end4_a:
tst r1, #2 /* 2 or more halfwords left? */
- ldrneh r3, [r2]
- ldrneh r4, [r2]
+ ldrhne r3, [r2]
+ ldrhne r4, [r2]
orrne r3, r3, r4, lsl #16
strne r3, [r0], #4
tst r1, #1 /* one halfword left? */
- ldrneh r3, [r2]
- strneh r3, [r0], #2
+ ldrhne r3, [r2]
+ strhne r3, [r0], #2
ldmpc regs=r4-r5
@@ -291,9 +291,9 @@ copy_write_sectors:
.w_end2_u:
tst r1, #1 /* one halfword left? */
- ldrneh r4, [r0], #2
+ ldrhne r4, [r0], #2
orrne r3, r3, r4, lsl #8
- strneh r3, [r2]
+ strhne r3, [r2]
movne r3, r3, lsr #16
ldrb r4, [r0], #1 /* load final byte */
@@ -305,8 +305,8 @@ copy_write_sectors:
/* 16-bit aligned */
.w_aligned:
tst r0, #2 /* 32 bit aligned? */
- ldrneh r3, [r0], #2 /* no: load first halfword */
- strneh r3, [r2] /* write */
+ ldrhne r3, [r0], #2 /* no: load first halfword */
+ strhne r3, [r2] /* write */
subne r1, r1, #1 /* one halfword taken */
sub r1, r1, #8 /* adjust for zero-check and doing 8 halfwords/loop */
@@ -341,13 +341,13 @@ copy_write_sectors:
tst r1, #2 /* 2 or more halfwords left? */
ldrne r3, [r0], #4
- strneh r3, [r2]
+ strhne r3, [r2]
movne r3, r3, lsr #16
- strneh r3, [r2]
+ strhne r3, [r2]
tst r1, #1 /* one halfword left? */
- ldrneh r3, [r0], #2
- strneh r3, [r2]
+ ldrhne r3, [r0], #2
+ strhne r3, [r2]
ldmpc regs=r4-r5
diff --git a/firmware/target/arm/ata-nand-telechips.c b/firmware/target/arm/ata-nand-telechips.c
index 73d92a5215..d61c223219 100644
--- a/firmware/target/arm/ata-nand-telechips.c
+++ b/firmware/target/arm/ata-nand-telechips.c
@@ -48,7 +48,7 @@ static long next_yield = 0;
static struct mutex ata_mtx SHAREDBSS_ATTR;
-#if defined(COWON_D2) || defined(IAUDIO_7)
+#if defined(COWON_D2)
#define FTL_V2
#define MAX_WRITE_CACHES 8
#else
@@ -906,13 +906,6 @@ int nand_init(void)
mutex_init(&ata_mtx);
-#ifdef CPU_TCC77X
- CSCFG2 = 0x018a8010 | tcc77x_cscfg_bw(TCC77X_CSCFG_BW8);
-
- GPIOC_FUNC &= ~(CS_GPIO_BIT | WE_GPIO_BIT);
- GPIOC_FUNC |= 0x1;
-#endif
-
/* Set GPIO direction for chip select & write protect */
NAND_GPIO_OUT_EN(CS_GPIO_BIT | WE_GPIO_BIT);
@@ -922,7 +915,7 @@ int nand_init(void)
#ifndef BOOTLOADER
/* Use chip info to allocate the correct size LPT buffer */
lptbuf_size = sizeof(struct lpt_entry) * segments_per_bank * total_banks;
- lpt_handle = core_alloc("lpt lookup", lptbuf_size);
+ lpt_handle = core_alloc(lptbuf_size);
struct lpt_entry* lpt_lookup = core_get_data(lpt_handle);
#else
/* Use a static array in the bootloader */
diff --git a/firmware/target/arm/imx233/creative-zen/lcd-zenmozaic.c b/firmware/target/arm/imx233/creative-zen/lcd-zenmozaic.c
index c1bc379a49..ab4466300b 100644
--- a/firmware/target/arm/imx233/creative-zen/lcd-zenmozaic.c
+++ b/firmware/target/arm/imx233/creative-zen/lcd-zenmozaic.c
@@ -149,6 +149,8 @@ void lcd_update_rect(int x, int y, int w, int h)
lcd_write_reg(0x17, y | (y + h - 1) << 8);
lcd_write_reg(0x21, y * LCD_WIDTH + x);
lcd_write_reg(0x22, 0);
+
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
for(int yy = y; yy < y + h; yy++)
- imx233_lcdif_pio_send(true, 2 * w, FBADDR(x, yy));
+ imx233_lcdif_pio_send(true, 2 * w, fbaddr(x,yy));
}
diff --git a/firmware/target/arm/imx233/creative-zen/lcd-zenv.c b/firmware/target/arm/imx233/creative-zen/lcd-zenv.c
index 06b0f158f3..75d2775814 100644
--- a/firmware/target/arm/imx233/creative-zen/lcd-zenv.c
+++ b/firmware/target/arm/imx233/creative-zen/lcd-zenv.c
@@ -172,8 +172,9 @@ void lcd_update_rect(int x, int y, int w, int h)
lcd_send(false, 0x75); lcd_send(true, y); lcd_send(true, y + h - 1);
lcd_send(false, 0x5c);
imx233_lcdif_set_word_length(16);
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
for(int yy = y; yy < y + h; yy++)
- imx233_lcdif_pio_send(true, w, FBADDR(x, yy));
+ imx233_lcdif_pio_send(true, w, fbaddr(x,yy));
}
#ifndef BOOTLOADER
diff --git a/firmware/target/arm/imx233/creative-zen/lcd-zenxfistyle.c b/firmware/target/arm/imx233/creative-zen/lcd-zenxfistyle.c
index ce0bcc3885..825b0072a3 100644
--- a/firmware/target/arm/imx233/creative-zen/lcd-zenxfistyle.c
+++ b/firmware/target/arm/imx233/creative-zen/lcd-zenxfistyle.c
@@ -282,8 +282,9 @@ void lcd_update_rect(int x, int y, int w, int h)
}
else
{
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
for(int i = 0; i < h; i++)
- memcpy((fb_data *)FRAME + i * w, FBADDR(x,y + i), w * sizeof(fb_data));
+ memcpy((fb_data *)FRAME + i * w, fbaddr(x,y + i), w * sizeof(fb_data));
}
/* WARNING The LCDIF has a limitation on the vertical count ! In 16-bit packed mode
* (which we used, ie 16-bit per pixel, 2 pixels per 32-bit words), the v_count
diff --git a/firmware/target/arm/imx233/creative-zenxfi2/lcd-zenxfi2.c b/firmware/target/arm/imx233/creative-zenxfi2/lcd-zenxfi2.c
index 890ff0b586..d0084900e7 100644
--- a/firmware/target/arm/imx233/creative-zenxfi2/lcd-zenxfi2.c
+++ b/firmware/target/arm/imx233/creative-zenxfi2/lcd-zenxfi2.c
@@ -241,8 +241,9 @@ void lcd_update_rect(int x, int y, int w, int h)
}
else
{
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
for(int i = 0; i < h; i++)
- memcpy((fb_data *)FRAME + i * w, FBADDR(x,y + i), w * sizeof(fb_data));
+ memcpy((fb_data *)FRAME + i * w, fbaddr(x,y + i), w * sizeof(fb_data));
}
/* WARNING The LCDIF has a limitation on the vertical count ! In 16-bit packed mode
* (which we used, ie 16-bit per pixel, 2 pixels per 32-bit words), the v_count
diff --git a/firmware/target/arm/imx233/creative-zenxfi3/lcd-zenxfi3.c b/firmware/target/arm/imx233/creative-zenxfi3/lcd-zenxfi3.c
index 59496f2d24..d5f25a523c 100644
--- a/firmware/target/arm/imx233/creative-zenxfi3/lcd-zenxfi3.c
+++ b/firmware/target/arm/imx233/creative-zenxfi3/lcd-zenxfi3.c
@@ -188,8 +188,9 @@ void lcd_update_rect(int x, int y, int w, int h)
}
else
{
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
for(int i = 0; i < h; i++)
- memcpy((fb_data *)FRAME + i * w, FBADDR(x,y + i), w * sizeof(fb_data));
+ memcpy((fb_data *)FRAME + i * w, fbaddr(x,y + i), w * sizeof(fb_data));
}
/* WARNING The LCDIF has a limitation on the vertical count ! In 16-bit packed mode
* (which we used, ie 16-bit per pixel, 2 pixels per 32-bit words), the v_count
diff --git a/firmware/target/arm/imx233/debug-imx233.c b/firmware/target/arm/imx233/debug-imx233.c
index bfc38b20dc..4487952162 100644
--- a/firmware/target/arm/imx233/debug-imx233.c
+++ b/firmware/target/arm/imx233/debug-imx233.c
@@ -940,8 +940,8 @@ bool dbg_hw_info_emi(void)
bool dbg_hw_info_audio(void)
{
- static const char *hp_sel[2] = {"DAC", "Line1"};
- static const char *mux_sel[4] = {"Mic", "Line1", "HP", "Line2"};
+ static const char * const hp_sel[2] = {"DAC", "Line1"};
+ static const char * const mux_sel[4] = {"Mic", "Line1", "HP", "Line2"};
lcd_setfont(FONT_SYSFIXED);
while(1)
@@ -1171,7 +1171,7 @@ bool dbg_hw_info_button(void)
}
else if(MAP[i].periph == IMX233_BUTTON_LRADC)
{
- static const char *op_name[] =
+ static const char * const op_name[] =
{
[IMX233_BUTTON_EQ] = "eq",
[IMX233_BUTTON_GT] = "gt",
diff --git a/firmware/target/arm/imx233/pcm-imx233.c b/firmware/target/arm/imx233/pcm-imx233.c
index c06120e4aa..1588d2c874 100644
--- a/firmware/target/arm/imx233/pcm-imx233.c
+++ b/firmware/target/arm/imx233/pcm-imx233.c
@@ -42,7 +42,6 @@ __ENSURE_STRUCT_CACHE_FRIENDLY(struct pcm_dma_command_t)
static int dac_locked = 0;
static struct pcm_dma_command_t dac_dma;
-static bool dac_freezed = false;
static const void *dac_buf; /* current buffer */
static size_t dac_size; /* remaining size */
@@ -175,17 +174,6 @@ void pcm_dma_apply_settings(void)
pcm_play_unlock();
}
-const void *pcm_play_dma_get_peak_buffer(int *count)
-{
- if(!dac_freezed)
- imx233_dma_freeze_channel(APB_AUDIO_DAC, true);
- struct imx233_dma_info_t info = imx233_dma_get_info(APB_AUDIO_DAC, DMA_INFO_AHB_BYTES | DMA_INFO_BAR);
- if(!dac_freezed)
- imx233_dma_freeze_channel(APB_AUDIO_DAC, false);
- *count = info.ahb_bytes;
- return (void *)info.bar;
-}
-
/*
* Recording
*/
diff --git a/firmware/target/arm/imx233/samsung-ypz5/audio-target.h b/firmware/target/arm/imx233/samsung-ypz5/audio-target.h
deleted file mode 100644
index c0bee26e45..0000000000
--- a/firmware/target/arm/imx233/samsung-ypz5/audio-target.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2013 by Lorenzo Miori
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ****************************************************************************/
-#ifndef __audio_target__
-#define __audio_target__
-
-/* MUTE_ON toggles a transistor that in turns toggles a mosfet... */
-#define IMX233_AUDIO_HP_GATE_BANK 1
-#define IMX233_AUDIO_HP_GATE_PIN 22
-#define IMX233_AUDIO_HP_GATE_INVERTED
-
-#define IMX233_AUDIO_COUPLING_MODE ACM_CAP
-
-#endif /* __audio_target__ */
diff --git a/firmware/target/arm/imx233/samsung-ypz5/backlight-target.h b/firmware/target/arm/imx233/samsung-ypz5/backlight-target.h
deleted file mode 100644
index e26f83811d..0000000000
--- a/firmware/target/arm/imx233/samsung-ypz5/backlight-target.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- *
- * Copyright (C) 2013 by Lorenzo Miori
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ****************************************************************************/
-#ifndef BACKLIGHT_TARGET_H
-#define BACKLIGHT_TARGET_H
-
-bool backlight_hw_init(void);
-void backlight_hw_on(void);
-void backlight_hw_off(void);
-void backlight_hw_brightness(int brightness);
-
-#endif /* BACKLIGHT_TARGET_H */
diff --git a/firmware/target/arm/imx233/samsung-ypz5/backlight-ypz5.c b/firmware/target/arm/imx233/samsung-ypz5/backlight-ypz5.c
deleted file mode 100644
index b02c3a1d16..0000000000
--- a/firmware/target/arm/imx233/samsung-ypz5/backlight-ypz5.c
+++ /dev/null
@@ -1,149 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- *
- * Copyright (C) 2013 by Lorenzo Miori
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ****************************************************************************/
-
-#include "config.h"
-#include "system.h"
-#include "lcd.h"
-#include "backlight.h"
-#include "backlight-target.h"
-#include "pinctrl-imx233.h"
-
-/**
- * AAT3151 Backlight Controller
- */
-
-/* Timings */
-#define TIME_OFF 500
-#define TIME_LOW 50
-#define TIME_HI 50
-#define TIME_LAT 500
-
-/* Number of raising edges to select the particular register */
-#define D1_D4_CURRENT_E 17
-#define D1_D3_CURRENT_E 18
-#define D4_CURRENT_E 19
-#define MAX_CURRENT_E 20
-#define LOW_CURRENT_E 21
-
-/* The actual register address / number */
-#define D1_D4_CURRENT 1
-#define D1_D3_CURRENT 2
-#define D4_CURRENT 3
-#define MAX_CURRENT 4
-#define LOW_CURRENT 5
-
-/* Valid values for LOW_CURRENT register */
-#define MAX_CURRENT_20 1
-#define MAX_CURRENT_30 2
-#define MAX_CURRENT_15 3
-#define MAX_CURRENT_LOW_CURRENT 4
-
-static int current_level = -1;
-
-static void create_raising_edges(int num)
-{
- while (num--)
- {
- /* Setting a register takes a sufficient small amount of time,
- * in the order of 50 ns. Thus the necessary 2 delays TIME_LOW/TIME_HI
- * are not strictly necessary */
- imx233_pinctrl_set_gpio(3, 13, false);
- imx233_pinctrl_set_gpio(3, 13, true);
- }
-}
-
-static void aat3151_write(int addr, int data)
-{
- create_raising_edges(16 + addr);
- udelay(TIME_LAT);
- create_raising_edges(data);
- udelay(TIME_LAT);
-}
-
-void backlight_hw_brightness(int level)
-{
- /* Don't try to reset backlight if not necessary
- * Moreover this helps to avoid flickering when
- * being in some screens like USB mode and
- * pressing some keys / touchpad...
- */
- if (current_level == level) return;
-
- /* Check for limits and adjust in case */
- level = MIN(MAX_BRIGHTNESS_SETTING, MAX(0, level));
-
- if (level == 0)
- {
- /* Set pin low for a sufficient time, puts the device into low-power consumption state
- * In other words backlight goes off
- */
- imx233_pinctrl_set_gpio(3, 13, false);
- udelay(TIME_OFF);
- }
- else
- {
- if (level > 3) {
- /* This enables 16 levels of backlight */
- aat3151_write(MAX_CURRENT, MAX_CURRENT_15);
- /* Set the value according Table 1 in datasheet
- * For MAX_CURRENT_15, the scale is from 0 mA to 15 mA in 16 steps
- */
- aat3151_write(D1_D3_CURRENT, 19 - level);
- }
- else {
- /* This enables other 4 levels of backlight */
- aat3151_write(MAX_CURRENT, MAX_CURRENT_LOW_CURRENT);
- /* Set the value according Table 1 in datasheet
- * For LOW_CURRENT, there is no "real" scale. We have scattered values.
- * We are interested in the last 3 -> 0.5 mA; 1 mA; 2 mA
- */
- aat3151_write(LOW_CURRENT, 13 + level);
- }
- }
- current_level = level;
-}
-
-bool backlight_hw_init(void)
-{
- imx233_pinctrl_acquire(3, 13, "backlight");
- imx233_pinctrl_set_function(3, 13, PINCTRL_FUNCTION_GPIO);
- imx233_pinctrl_set_drive(3, 13, PINCTRL_DRIVE_4mA);
- imx233_pinctrl_enable_gpio(3, 13, true);
- imx233_pinctrl_set_gpio(3, 13, false);
- return true;
-}
-
-void backlight_hw_on(void)
-{
-#ifdef HAVE_LCD_ENABLE
- lcd_enable(true); /* power on lcd + visible display */
-#endif
- /* restore the previous backlight level */
- backlight_hw_brightness(backlight_brightness);
-}
-
-void backlight_hw_off(void)
-{
- /* there is no real on/off but we can set to 0 brightness */
- backlight_hw_brightness(0);
-#ifdef HAVE_LCD_ENABLE
- lcd_enable(false); /* power off visible display */
-#endif
-}
diff --git a/firmware/target/arm/imx233/samsung-ypz5/button-target.h b/firmware/target/arm/imx233/samsung-ypz5/button-target.h
deleted file mode 100644
index 2f94f5fb31..0000000000
--- a/firmware/target/arm/imx233/samsung-ypz5/button-target.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2013 by Lorenzo Miori
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ****************************************************************************/
-#ifndef _BUTTON_TARGET_H_
-#define _BUTTON_TARGET_H_
-
-#include <stdbool.h>
-
-#define HAS_BUTTON_HOLD
-#define IMX233_BUTTON_LRADC_CHANNEL 0
-#define IMX233_BUTTON_LRADC_HOLD_DET BLH_GPIO
-#define BLH_GPIO_BANK 0
-#define BLH_GPIO_PIN 13
-
-#define IMX233_BUTTON_LRADC_CHANNEL 0
-
-/* Main unit's buttons */
-#define BUTTON_POWER 0x00000001
-#define BUTTON_VOL_UP 0x00000002
-#define BUTTON_VOL_DOWN 0x00000004
-/* Directional buttons by touchpad */
-#define BUTTON_LEFT 0x00000008
-#define BUTTON_UP 0x00000010
-#define BUTTON_RIGHT 0x00000020
-#define BUTTON_DOWN 0x00000040
-#define BUTTON_SELECT 0x00000080
-#define BUTTON_BACK 0x00000100
-#define BUTTON_REW 0x00000200
-#define BUTTON_FF 0x00000400
-
-
-#define BUTTON_MAIN (BUTTON_VOL_UP | BUTTON_VOL_DOWN | BUTTON_POWER | BUTTON_LEFT | \
- BUTTON_UP | BUTTON_RIGHT | BUTTON_DOWN | BUTTON_SELECT | \
- BUTTON_BACK | BUTTON_REW | BUTTON_FF)
-
-/* Software power-off */
-#define POWEROFF_BUTTON BUTTON_POWER
-#define POWEROFF_COUNT 10
-
-bool button_debug_screen(void);
-
-#endif /* _BUTTON_TARGET_H_ */
diff --git a/firmware/target/arm/imx233/samsung-ypz5/button-ypz5.c b/firmware/target/arm/imx233/samsung-ypz5/button-ypz5.c
deleted file mode 100644
index 91fe059c31..0000000000
--- a/firmware/target/arm/imx233/samsung-ypz5/button-ypz5.c
+++ /dev/null
@@ -1,273 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- *
- * Copyright (C) 2013 by Lorenzo Miori
- *
- * 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 "lcd.h"
-#include "string.h"
-#include "kernel.h"
-#include "pinctrl-imx233.h"
-#include "power-imx233.h"
-#include "button-lradc-imx233.h"
-#include "button-target.h"
-#include "button-imx233.h"
-
-#ifndef BOOTLOADER
-#include "touchscreen.h"
-#include "touchscreen-imx233.h"
-#include "button.h"
-#include "font.h"
-#include "action.h"
-#endif
-
-#define CHAN 0 /* ADC channel for the buttons */
-#define I_VDDIO 0 /* Mock button to define the relative voltage to compute voltage from ADC steps */
-
-struct imx233_button_map_t imx233_button_map[] =
-{
- [I_VDDIO] = IMX233_BUTTON_(VDDIO, VDDIO(3760), "vddio"), /* we need VDDIO for relative */
- IMX233_BUTTON_(HOLD, GPIO(0, 13), "hold"),
- IMX233_BUTTON(POWER, PSWITCH(1), "power"),
- IMX233_BUTTON(SELECT, PSWITCH(3), "select"),
- IMX233_BUTTON(VOL_UP, LRADC_REL(CHAN, 485, I_VDDIO), "vol up"),
- IMX233_BUTTON(VOL_DOWN, LRADC_REL(CHAN, 975, I_VDDIO), "vol down"),
- IMX233_BUTTON(BACK, LRADC_REL(CHAN, 1521, I_VDDIO), "back"),
- IMX233_BUTTON(FF, LRADC_REL(CHAN, 2000, I_VDDIO), "ff"),
- IMX233_BUTTON(REW, LRADC_REL(CHAN, 2480, I_VDDIO), "rew"),
- IMX233_BUTTON_(END, END(), "")
-};
-
-#ifndef BOOTLOADER
-static int last_x = 0;
-static int last_y = 0;
-static bool touching = false;
-#endif /* BOOTLOADER */
-
-#ifndef BOOTLOADER
-
-/* Touchpad extra pin initialization
- * Strange facts:
- * 1. In the fully working sample I have, it seems that pins
- * must be all set to low
- * 2. In the other sample without LCD, it seems (by measurement) that
- * not all the pins are set to low! Actually, I still need to see if
- * touchpad works in this other sample.
-*/
-void touchpad_pin_setup(void)
-{
- /* TX+ */
- imx233_pinctrl_acquire(0, 25, "touchpad X+ power low");
- imx233_pinctrl_set_function(0, 25, PINCTRL_FUNCTION_GPIO);
- imx233_pinctrl_set_drive(0, 25, PINCTRL_DRIVE_4mA);
- imx233_pinctrl_enable_gpio(0, 25, true);
-
- /* TY+ */
- imx233_pinctrl_acquire(0, 26, "touchpad Y+ power high");
- imx233_pinctrl_set_function(0, 26, PINCTRL_FUNCTION_GPIO);
- imx233_pinctrl_set_drive(0, 26, PINCTRL_DRIVE_4mA);
- imx233_pinctrl_enable_gpio(0, 26, true);
-
- /* TY- */
- imx233_pinctrl_acquire(1, 21, "touchpad Y- power low");
- imx233_pinctrl_set_function(1, 21, PINCTRL_FUNCTION_GPIO);
- imx233_pinctrl_set_drive(1, 21, PINCTRL_DRIVE_4mA);
- imx233_pinctrl_enable_gpio(1, 21, true);
-
- /* TX- */
- imx233_pinctrl_acquire(3, 15, "touchpad X- power high");
- imx233_pinctrl_set_function(3, 15, PINCTRL_FUNCTION_GPIO);
- imx233_pinctrl_set_drive(3, 15, PINCTRL_DRIVE_4mA);
- imx233_pinctrl_enable_gpio(3, 15, true);
-
-}
-#endif /* BOOTLOADER */
-
-void button_init_device(void)
-{
- /* init button subsystem */
- imx233_button_init();
-#ifndef BOOTLOADER
- touchpad_pin_setup();
- /* Now that is powered up, proceed with touchpad initialization */
- imx233_touchscreen_init();
- imx233_touchscreen_enable(true);
-#endif /* BOOTLOADER */
-}
-
-/* X, Y, RadiusX, RadiusY */
-#define TOUCH_UP 2400, 1050, 650, 250
-#define TOUCH_DOWN 2057, 3320, 500, 350
-#define TOUCH_LEFT 3581, 2297, 300, 350
-#define TOUCH_RIGHT 1000, 2100, 400, 700
-#define TOUCH_CENTER 2682, 2167, 335, 276
-
-bool coord_in_radius(int x, int y, int cx, int cy, int rx, int ry)
-{
- return ((x >= cx - rx && x <= cx + rx) && (y >= cy - ry && y <= cy + ry));
-}
-
-int button_read_device(void)
-{
- int res = 0;
-
-#ifndef BOOTLOADER
- /* handle the touchpad events */
- touching = imx233_touchscreen_get_touch(&last_x, &last_y);
- if(touching)
- {
- if (coord_in_radius(last_x, last_y, TOUCH_LEFT))
- {
- res |= BUTTON_LEFT;
- }
- else if (coord_in_radius(last_x, last_y, TOUCH_RIGHT))
- {
- res |= BUTTON_RIGHT;
- }
- else if (coord_in_radius(last_x, last_y, TOUCH_DOWN))
- {
- res |= BUTTON_DOWN;
- }
- else if (coord_in_radius(last_x, last_y, TOUCH_UP))
- {
- res |= BUTTON_UP;
- }
- }
-#endif /* BOOTLOADER */
- /* handle the generic events */
- return imx233_button_read(res);
-}
-
-#ifndef BOOTLOADER
-
-#define MAX_ENTRIES 100
-#define VIEWPORT_HEIGHT 100
-#define VIEWPORT_WIDTH 100
-#define MAX_X 3700
-#define MAX_Y 3700
-#define ADAPT_TO_VIEWPORT(cx, cy, rx, ry) ((float)(cx) / MAX_X) * VIEWPORT_WIDTH, \
- ((float)(cy) / MAX_Y) * VIEWPORT_HEIGHT, \
- ((float)(rx) / MAX_X) * VIEWPORT_WIDTH, \
- ((float)(ry) / MAX_Y) * VIEWPORT_HEIGHT
-static void draw_calibration_rect(int cx, int cy, int rx, int ry)
-{
- if (coord_in_radius(last_x, last_y, cx, cy, rx, ry))
- lcd_set_drawinfo(DRMODE_SOLID, LCD_RGBPACK(0xff, 0xff, 0xff), LCD_BLACK);
- else
- lcd_set_drawinfo(DRMODE_SOLID, LCD_RGBPACK(0xff, 0, 0), LCD_BLACK);
- lcd_drawrect(ADAPT_TO_VIEWPORT(cx - rx, cy - ry, 2 * rx, 2 * ry));
-}
-
-bool button_debug_screen(void)
-{
- int last = 0;
- struct point_t
- {
- int x;
- int y;
- };
- struct point_t last_entries[MAX_ENTRIES];
- struct viewport report_vp;
-
- memset(&report_vp, 0, sizeof(report_vp));
- report_vp.x = (LCD_WIDTH - VIEWPORT_WIDTH) / 2;
- report_vp.y = (LCD_HEIGHT - VIEWPORT_HEIGHT) / 2;
- report_vp.width = VIEWPORT_WIDTH;
- report_vp.height = VIEWPORT_HEIGHT;
-
- lcd_setfont(FONT_SYSFIXED);
- lcd_clear_display();
-
- while(1)
- {
- int button = get_action(CONTEXT_STD, HZ / 10);
- switch(button)
- {
- case ACTION_STD_OK:
- case ACTION_STD_MENU:
- lcd_set_viewport(NULL);
- lcd_setfont(FONT_UI);
- lcd_clear_display();
- return true;
- case ACTION_STD_CANCEL:
- lcd_set_viewport(NULL);
- lcd_setfont(FONT_UI);
- lcd_clear_display();
- return false;
- }
-
- lcd_set_viewport(NULL);
- lcd_putsf(0, 1, "(%d,%d) %s", last_x, last_y, touching ? "touching!" : "");
- lcd_putsf(0, 0, "Type %s", imx233_pinctrl_get_gpio(0, 31) ? "CAP" : "REG");
- lcd_set_viewport(&report_vp);
- lcd_set_drawinfo(DRMODE_SOLID, LCD_RGBPACK(0, 0, 0xff), LCD_BLACK);
- lcd_drawrect(0, 0, 100, 100);
- float percent_x = ((float)(last_x) / MAX_X);
- float percent_y = ((float)(last_y) / MAX_Y);
- if (touching)
- {
- lcd_set_viewport(NULL);
- if (last < MAX_ENTRIES)
- {
- last_entries[last].x = last_x;
- last_entries[last].y = last_y;
- last++;
- lcd_putsf(0, 17, "Recording: %d captures left", MAX_ENTRIES - last);
- }
- else
- {
- int min_x = 9999;
- int min_y = 9999;
- int max_x = -1;
- int max_y = -1;
- int median_x = 0;
- int median_y = 0;
- for (int i = 0; i < MAX_ENTRIES; i++)
- {
- min_x = MIN(min_x, last_entries[i].x);
- min_y = MIN(min_y, last_entries[i].y);
- max_x = MAX(max_x, last_entries[i].x);
- max_y = MAX(max_y, last_entries[i].y);
- median_x += last_entries[i].x;
- median_y += last_entries[i].y;
- }
- median_x /= MAX_ENTRIES;
- median_y /= MAX_ENTRIES;
- lcd_putsf(0, 17, "center(%d,%d)", median_x, median_y);
- lcd_putsf(0, 18, "radius(%d,%d)", median_x / 2, median_y / 2);
- }
- lcd_set_viewport(&report_vp);
- lcd_set_drawinfo(DRMODE_SOLID, LCD_RGBPACK(0xff, 0x8c, 0), LCD_BLACK);
- lcd_fillrect(VIEWPORT_WIDTH * percent_x, VIEWPORT_HEIGHT * percent_y, 2, 2);
- }
-
- /* Draw current calibration settings */
- lcd_set_viewport(&report_vp);
- draw_calibration_rect(TOUCH_UP);
- draw_calibration_rect(TOUCH_DOWN);
- draw_calibration_rect(TOUCH_CENTER);
- draw_calibration_rect(TOUCH_LEFT);
- draw_calibration_rect(TOUCH_RIGHT);
-
- lcd_update();
- yield();
- }
-
- return true;
-}
-#endif
diff --git a/firmware/target/arm/imx233/samsung-ypz5/fmradio-target.h b/firmware/target/arm/imx233/samsung-ypz5/fmradio-target.h
deleted file mode 100644
index 66bceb4071..0000000000
--- a/firmware/target/arm/imx233/samsung-ypz5/fmradio-target.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2013 by Amaury Pouly
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ****************************************************************************/
-#ifndef _FMRADIO_TARGET_H_
-#define _FMRADIO_TARGET_H_
-
-#define IMX233_FMRADIO_I2C FMI_HW
-
-#define IMX233_FMRADIO_POWER FMP_GPIO
-#define FMP_GPIO_BANK 0
-#define FMP_GPIO_PIN 10
-#define FMP_GPIO_DELAY (HZ / 5)
-
-#endif /* _FMRADIO_TARGET_H_ */
diff --git a/firmware/target/arm/imx233/samsung-ypz5/lcd-target.h b/firmware/target/arm/imx233/samsung-ypz5/lcd-target.h
deleted file mode 100644
index 9993b7d731..0000000000
--- a/firmware/target/arm/imx233/samsung-ypz5/lcd-target.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- *
- * Copyright (c) 2013 by Amaury Pouly
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ****************************************************************************/
-#ifndef LCD_TARGET_H
-#define LCD_TARGET_H
-
-bool lcd_debug_screen(void);
-
-#endif /* LCD_TARGET_H */
diff --git a/firmware/target/arm/imx233/samsung-ypz5/lcd-ypz5.c b/firmware/target/arm/imx233/samsung-ypz5/lcd-ypz5.c
deleted file mode 100644
index 99fe0b2aef..0000000000
--- a/firmware/target/arm/imx233/samsung-ypz5/lcd-ypz5.c
+++ /dev/null
@@ -1,295 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- *
- * Copyright (c) 2013 by Lorenzo Miori
- *
- * 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 <sys/types.h> /* off_t */
-#include <string.h>
-#include "cpu.h"
-#include "system.h"
-#include "backlight-target.h"
-#include "lcd.h"
-#include "lcdif-imx233.h"
-#include "clkctrl-imx233.h"
-#include "pinctrl-imx233.h"
-#include "dcp-imx233.h"
-#include "logf.h"
-#ifndef BOOTLOADER
-#include "button.h"
-#include "font.h"
-#include "action.h"
-#endif
-#include "dma-imx233.h"
-#include "kernel.h"
-
-#include "regs/lcdif.h"
-
-/**
- * NOTE
- * We don't know exact LCD models nor we have datasheets for them
- * Register function are partly guessed from the values, others are guessed from other LCD
- * drivers and others have been confirmed studying their values
- */
-
-static enum lcd_type_t
-{
- LCD_TYPE_ZERO = 0,
- LCD_TYPE_ONE = 1
-} lcd_type = LCD_TYPE_ZERO;
-
-static void lcd_write_reg(uint16_t reg, uint16_t data)
-{
- imx233_lcdif_pio_send(false, 1, &reg);
- if(reg != 0x22)
- imx233_lcdif_pio_send(true, 1, &data);
-}
-
-/*
- * The two LCD types require different initialization sequences
- */
-void lcd_init_seq(void)
-{
- switch (lcd_type)
- {
- case LCD_TYPE_ZERO:
- {
- lcd_write_reg(0x11, 0x1f1e);
- lcd_write_reg(0x38, 0xf0f);
- lcd_write_reg(0x12, 0x1101);
- lcd_write_reg(0x13, 0x808);
- lcd_write_reg(0x14, 0x3119);
- lcd_write_reg(0x10, 0x1a10);
- udelay(0xc350);
- lcd_write_reg(0x13, 0x83b);
- udelay(0x30d40);
- lcd_write_reg(1, 0x90c); /* Display mode */
- lcd_write_reg(2, 0x200);
- lcd_write_reg(3, 0x1030);
- lcd_write_reg(7, 5);
- lcd_write_reg(8, 0x503);
- lcd_write_reg(11, 0);
- lcd_write_reg(12, 0);
- /* Gamma control */
- lcd_write_reg(0x30, 0x606);
- lcd_write_reg(0x31, 0x606);
- lcd_write_reg(0x32, 0x305);
- lcd_write_reg(0x33, 2);
- lcd_write_reg(0x34, 0x503);
- lcd_write_reg(0x35, 0x606);
- lcd_write_reg(0x36, 0x606);
- lcd_write_reg(0x37, 0x200);
-
- lcd_write_reg(0x11, 0x1f1e);
- lcd_write_reg(0x38, 0xf0f);
- /* Set initial LCD limits and RAM settings */
- lcd_write_reg(0x40, 0); //BPP ?
- lcd_write_reg(0x42, 0x9f00);
- lcd_write_reg(0x43, 0);
- lcd_write_reg(0x44, 0x7f00); /* Horizontal initial refresh zone [0 - 127] */
- lcd_write_reg(0x45, 0x9f00); /* Vertical initial refresh zone [0 - 159] */
-
- lcd_write_reg(14, 0x13);
- lcd_write_reg(0xa9, 0x14);
- lcd_write_reg(0xa7, 0x30);
- lcd_write_reg(0xa8, 0x124);
- lcd_write_reg(0x6f, 0x1d00);
- lcd_write_reg(0x70, 3);
- lcd_write_reg(7, 1);
- lcd_write_reg(0x10, 0x1a10);
- udelay(0x9c40);
- lcd_write_reg(7, 0x21);
- lcd_write_reg(7, 0x23);
- udelay(0x9c40);
- lcd_write_reg(7, 0x37); /* Seems to be "power on" */
- break;
- }
- case LCD_TYPE_ONE:
- {
- lcd_write_reg(0, 1);
- udelay(0x2710);
- lcd_write_reg(0x11, 0x171b);
- lcd_write_reg(0x12, 0);
- lcd_write_reg(0x13, 0x80d);
- lcd_write_reg(0x14, 0x18);
- lcd_write_reg(0x10, 0x1a10);
- udelay(0xc350);
- lcd_write_reg(0x13, 0x81d);
- udelay(0xc350);
- lcd_write_reg(1, 0x90c); /* Display mode */
- lcd_write_reg(2, 0x200);
- lcd_write_reg(3, 0x1030);
- lcd_write_reg(7, 5);
- lcd_write_reg(8, 0x30a);
- lcd_write_reg(11, 4);
- lcd_write_reg(12, 0);
- /* Gamma control */
- lcd_write_reg(0x30, 0x300);
- lcd_write_reg(0x31, 0);
- lcd_write_reg(0x32, 0);
- lcd_write_reg(0x33, 0x404);
- lcd_write_reg(0x34, 0x707);
- lcd_write_reg(0x35, 0x700);
- lcd_write_reg(0x36, 0x703);
- lcd_write_reg(0x37, 4);
-
- lcd_write_reg(0x38, 0);
- /* Set initial LCD limits and RAM settings */
- lcd_write_reg(0x40, 0);
- lcd_write_reg(0x42, 0x9f00); /* LCD Display Start Address Register 0 */
- lcd_write_reg(0x43, 0); /* LCD Display Start Address Register 1 */
- lcd_write_reg(0x44, 0x7f00); /* Horizontal initial refresh zone [0 - 127] */
- lcd_write_reg(0x45, 0x9f00); /* Vertical initial refresh zone [0 - 159] */
-
- lcd_write_reg(7, 1);
- udelay(0x2710);
- lcd_write_reg(7, 0x21);
- lcd_write_reg(7, 0x23);
- udelay(0x2710);
- lcd_write_reg(7, 0x1037);
- udelay(0x2710);
- lcd_write_reg(7, 0x35);
- lcd_write_reg(7, 0x36);
- lcd_write_reg(7, 0x37);
- udelay(10000);
- break;
- }
- default:
- break;
- }
-}
-
-static void send_update_rect(uint8_t x, uint8_t y, uint8_t w, uint8_t h)
-{
- /* Set horizontal refresh zone */
- lcd_write_reg(0x44, (x | (y + w - 1) << 0x8));
- /* Set vertical refresh zone */
- lcd_write_reg(0x45, (y | (y + h - 1) << 0x8));
- lcd_write_reg(0x21, x | y << 8);
- /* Set register index to 0x22 to write screen data. 0 is mock value */
- lcd_write_reg(0x22, 0);
-}
-
-static void setup_lcd_pins(void)
-{
- imx233_lcdif_setup_system_pins(16);
- /* lcd_rd */
- imx233_pinctrl_acquire(0, 9, "lcd rd");
- imx233_pinctrl_set_function(0, 9, PINCTRL_FUNCTION_GPIO);
- imx233_pinctrl_set_gpio(0, 9, false);
- /*
- * This pin is important to know the LCD type
- * There are two types that require two different initialization sequences
- */
- /* lcd_tp */
- imx233_pinctrl_acquire(3, 12, "lcd type");
- imx233_pinctrl_set_function(3, 12, PINCTRL_FUNCTION_GPIO);
- imx233_pinctrl_enable_gpio(3, 12, false);
- /* Sense LCD Type */
- lcd_type = imx233_pinctrl_get_gpio(3, 12) ? LCD_TYPE_ONE : LCD_TYPE_ZERO;
-}
-
-static void setup_parameters(void)
-{
- imx233_lcdif_init();
- imx233_lcdif_enable(true);
- imx233_lcdif_set_word_length(16);
- imx233_lcdif_set_data_swizzle(false);
- imx233_lcdif_set_timings(2, 1, 1, 1);
- BF_WR(LCDIF_CTRL, MODE86_V(8080_MODE));
-
- imx233_lcdif_reset_lcd(true);
- udelay(50);
- imx233_lcdif_reset_lcd(false);
- udelay(10);
- imx233_lcdif_reset_lcd(true);
-}
-
-void lcd_init_device(void)
-{
- /* Setup interface pins */
- setup_lcd_pins();
- /* Set LCD parameters */
- setup_parameters();
- /* Send initialization sequence to LCD */
- lcd_init_seq();
-}
-
-struct lcdif_cmd_t
-{
- struct apb_dma_command_t dma;
- uint32_t ctrl0;
- uint32_t pad[4];
-} __attribute__((packed)) CACHEALIGN_ATTR;
-
-struct lcdif_cmd_t lcdif_dma;
-void lcd_update(void)
-{
- unsigned size = LCD_WIDTH * LCD_HEIGHT * sizeof(fb_data);
-
- send_update_rect(0,0,LCD_WIDTH,LCD_HEIGHT);
- /* We can safely do the transfer in a single shot, since 160 * 128 * 2 < 65k,
- * the maximum transfer size!
- */
- lcdif_dma.dma.cmd |= BF_OR(APB_CHx_CMD, CMDWORDS(1), XFER_COUNT(size), COMMAND(2));
- lcdif_dma.ctrl0 = HW_LCDIF_CTRL & ~BM_LCDIF_CTRL_COUNT;
- lcdif_dma.ctrl0 |= BF_OR(LCDIF_CTRL, COUNT(size/2), DATA_SELECT(1));
- lcdif_dma.dma.buffer = FBADDR(0,0);
- lcdif_dma.dma.cmd |= BM_APB_CHx_CMD_SEMAPHORE;
-
- imx233_dma_start_command(APB_LCDIF, &lcdif_dma.dma);
- imx233_dma_wait_completion(APB_LCDIF, HZ);
-}
-
-void lcd_update_rect(int x, int y, int w, int h)
-{
- (void)x;
- (void)y;
- (void)w;
- (void)h;
- lcd_update();
-}
-
-#ifndef BOOTLOADER
-bool lcd_debug_screen(void)
-{
- lcd_setfont(FONT_SYSFIXED);
-
- while(1)
- {
- int button = get_action(CONTEXT_STD, HZ / 10);
- switch(button)
- {
- case ACTION_STD_NEXT:
- case ACTION_STD_PREV:
- case ACTION_STD_OK:
- case ACTION_STD_MENU:
- lcd_setfont(FONT_UI);
- return true;
- case ACTION_STD_CANCEL:
- lcd_setfont(FONT_UI);
- return false;
- }
-
- lcd_clear_display();
- lcd_putsf(0, 0, "LCD type: %d", lcd_type);
- lcd_update();
- yield();
- }
-
- return true;
-}
-#endif
diff --git a/firmware/target/arm/imx233/samsung-ypz5/powermgmt-target.h b/firmware/target/arm/imx233/samsung-ypz5/powermgmt-target.h
deleted file mode 100644
index 5d32bc5e9c..0000000000
--- a/firmware/target/arm/imx233/samsung-ypz5/powermgmt-target.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2013 by Amaury Pouly
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ****************************************************************************/
-#ifndef POWERMGMT_TARGET_H
-#define POWERMGMT_TARGET_H
-
-#include "config.h"
-#include "powermgmt-imx233.h"
-
-#define IMX233_CHARGE_CURRENT 100
-#define IMX233_STOP_CURRENT 10
-#define IMX233_TOPOFF_TIMEOUT (30 * 60 * HZ)
-#define IMX233_CHARGING_TIMEOUT (4 * 3600 * HZ)
-#define IMX233_DIE_TEMP_HIGH 71
-#define IMX233_DIE_TEMP_LOW 56
-#define IMX233_BATT_TEMP_SENSOR 0
-#define IMX233_BATT_TEMP_HIGH 1100
-#define IMX233_BATT_TEMP_LOW 220
-
-#endif /* POWERMGMT_TARGET_H */
diff --git a/firmware/target/arm/imx233/samsung-ypz5/powermgmt-ypz5.c b/firmware/target/arm/imx233/samsung-ypz5/powermgmt-ypz5.c
deleted file mode 100644
index 9d9ed7de99..0000000000
--- a/firmware/target/arm/imx233/samsung-ypz5/powermgmt-ypz5.c
+++ /dev/null
@@ -1,48 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2013 by Amaury Pouly
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ****************************************************************************/
-#include "config.h"
-#include "powermgmt-target.h"
-#include "power-imx233.h"
-
-
-const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] =
-{
- 3400
-};
-
-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] =
-{
- /* Sansa Fuze+ Li Ion 600mAH figured from discharge curve */
- { 3100, 3650, 3720, 3750, 3780, 3820, 3880, 4000, 4040, 4125, 4230 },
-};
-
-/* voltages (millivolt) of 0%, 10%, ... 100% when charging enabled */
-const unsigned short percent_to_volt_charge[11] =
-{
- /* Sansa Fuze+ Li Ion 600mAH figured from charge curve */
- 3480, 3790, 3845, 3880, 3900, 3935, 4005, 4070, 4150, 4250, 4335
-};
diff --git a/firmware/target/arm/imx233/sansa-fuzeplus/lcd-fuzeplus.c b/firmware/target/arm/imx233/sansa-fuzeplus/lcd-fuzeplus.c
index 92864c9ed7..ceb7b4e090 100644
--- a/firmware/target/arm/imx233/sansa-fuzeplus/lcd-fuzeplus.c
+++ b/firmware/target/arm/imx233/sansa-fuzeplus/lcd-fuzeplus.c
@@ -637,8 +637,9 @@ void lcd_update_rect(int x, int y, int w, int h)
}
else
{
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
for(int i = 0; i < h; i++)
- memcpy((fb_data *)FRAME + i * w, FBADDR(x,y + i), w * sizeof(fb_data));
+ memcpy((fb_data *)FRAME + i * w, fbaddr(x,y + i), w * sizeof(fb_data));
}
/* WARNING The LCDIF has a limitation on the vertical count ! In 16-bit packed mode
* (which we used, ie 16-bit per pixel, 2 pixels per 32-bit words), the v_count
diff --git a/firmware/target/arm/imx233/sony-nwz/lcd-nwze360.c b/firmware/target/arm/imx233/sony-nwz/lcd-nwze360.c
index cfcf85bfc0..8f49bfa3eb 100644
--- a/firmware/target/arm/imx233/sony-nwz/lcd-nwze360.c
+++ b/firmware/target/arm/imx233/sony-nwz/lcd-nwze360.c
@@ -228,8 +228,9 @@ void lcd_update_rect(int x, int y, int w, int h)
}
else
{
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
for(int i = 0; i < h; i++)
- memcpy((fb_data *)FRAME + i * w, FBADDR(x,y + i), w * sizeof(fb_data));
+ memcpy((fb_data *)FRAME + i * w, fbaddr(x,y + i), w * sizeof(fb_data));
}
/* WARNING The LCDIF has a limitation on the vertical count ! In 16-bit packed mode
* (which we used, ie 16-bit per pixel, 2 pixels per 32-bit words), the v_count
diff --git a/firmware/target/arm/imx233/sony-nwz/lcd-nwze370.c b/firmware/target/arm/imx233/sony-nwz/lcd-nwze370.c
index 999f4ee525..862522da15 100644
--- a/firmware/target/arm/imx233/sony-nwz/lcd-nwze370.c
+++ b/firmware/target/arm/imx233/sony-nwz/lcd-nwze370.c
@@ -189,8 +189,9 @@ void lcd_update_rect(int x, int y, int w, int h)
}
else
{
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
for(int i = 0; i < h; i++)
- memcpy((fb_data *)FRAME + i * w, FBADDR(x,y + i), w * sizeof(fb_data));
+ memcpy((fb_data *)FRAME + i * w, fbaddr(x,y + i), w * sizeof(fb_data));
}
/* WARNING The LCDIF has a limitation on the vertical count ! In 16-bit packed mode
* (which we used, ie 16-bit per pixel, 2 pixels per 32-bit words), the v_count
diff --git a/firmware/target/arm/imx233/touchscreen-imx233.c b/firmware/target/arm/imx233/touchscreen-imx233.c
index f98dc9b738..561b2c750a 100644
--- a/firmware/target/arm/imx233/touchscreen-imx233.c
+++ b/firmware/target/arm/imx233/touchscreen-imx233.c
@@ -23,9 +23,6 @@
#include "kernel.h"
#include "touchscreen-imx233.h"
#include "stdlib.h"
-#ifdef SAMSUNG_YPZ5
-#include "pinctrl-imx233.h"
-#endif
/* Description:
* the driver basically has 2 modes:
@@ -82,24 +79,6 @@ static void touch_channel_irq(int chan)
process();
}
-#ifdef SAMSUNG_YPZ5
-/* On this target we need to manually setup pulldown pins,
- * using specific GPIO lines
- */
-static void pulldown_setup(bool xminus_enable, bool yminus_enable,
- bool xplus_enable, bool yplus_enable)
-{
- /* TX+ */
- imx233_pinctrl_set_gpio(0, 25, xplus_enable);
- /* TX- */
- imx233_pinctrl_set_gpio(3, 15, xminus_enable);
- /* TY+ */
- imx233_pinctrl_set_gpio(0, 26, yplus_enable);
- /* TY- */
- imx233_pinctrl_set_gpio(1, 21, yminus_enable);
-}
-#endif
-
static void kick_measure(bool pull_x, bool pull_y, bool detect, int src)
{
#if IMX233_SUBTARGET < 3700
@@ -116,9 +95,6 @@ static void kick_measure(bool pull_x, bool pull_y, bool detect, int src)
imx233_icoll_enable_interrupt(INT_SRC_LRADC_CHx(touch_chan), true);
imx233_lradc_enable_channel_irq(touch_chan, true);
/* setup measurement: x- pull down and x+ pull up */
-#ifdef SAMSUNG_YPZ5
- pulldown_setup(pull_x, pull_y, pull_x, pull_y);
-#endif
imx233_lradc_setup_touch(pull_x, pull_y, pull_x, pull_y, detect);
imx233_lradc_enable_touch_detect_irq(false);
imx233_lradc_enable_channel_irq(touch_chan, true);
@@ -138,9 +114,6 @@ static void enter_state(enum touch_state_t state)
switch(state)
{
case TOUCH_STATE_WAIT:
-#ifdef SAMSUNG_YPZ5
- pulldown_setup(false, false, false, false);
-#endif
imx233_lradc_setup_touch(false, false, false, false, true);
imx233_lradc_enable_touch_detect_irq(true);
break;
diff --git a/firmware/target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c
index 5b0c71110d..c84f1cf41c 100644
--- a/firmware/target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c
+++ b/firmware/target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c
@@ -125,53 +125,30 @@ bool si4700_st(void)
return (GPIO1_DR & (1 << 28)) >> 28;
}
-
/* Low-level RDS Support */
-static bool int_restore;
-
-/* Called after I2C read cycle completes */
-static void si4700_rds_read_raw_async_callback(struct i2c_transfer_desc *xfer)
-{
- if (xfer->rxcount == 0)
- si4700_rds_process();
- /* else read didn't finish */
-
- if (int_restore)
- gpio_int_enable(SI4700_EVENT_ID);
-}
-
-/* Called to read registers from ISR context */
-void si4700_rds_read_raw_async(unsigned char *buf, int count)
-{
- /* transfer descriptor for RDS async operations */
- static struct i2c_transfer_desc xfer = { .node = &si4700_i2c_node };
-
- xfer.txdata = NULL;
- xfer.txcount = 0;
- xfer.rxdata = buf;
- xfer.rxcount = count;
- xfer.callback = si4700_rds_read_raw_async_callback;
- xfer.next = NULL;
-
- i2c_transfer(&xfer);
-}
+static struct semaphore rds_sema;
+static uint32_t rds_stack[DEFAULT_STACK_SIZE/sizeof(uint32_t)];
/* RDS GPIO interrupt handler - start RDS data read */
void INT_SI4700_RDS(void)
{
- /* mask and clear the interrupt until we're done */
- gpio_int_disable(SI4700_EVENT_ID);
gpio_int_clear(SI4700_EVENT_ID);
+ semaphore_release(&rds_sema);
+}
- /* tell radio driver about it */
- si4700_rds_interrupt();
+/* Captures RDS data and processes it */
+static void NORETURN_ATTR rds_thread(void)
+{
+ while (true) {
+ semaphore_wait(&rds_sema, TIMEOUT_BLOCK);
+ si4700_rds_process();
+ }
}
/* Called with on=true after full radio power up, and with on=false before
powering down */
void si4700_rds_powerup(bool on)
{
- int_restore = on;
gpio_int_disable(SI4700_EVENT_ID);
gpio_int_clear(SI4700_EVENT_ID);
gpio_enable_event(SI4700_EVENT_ID, on);
@@ -180,5 +157,7 @@ void si4700_rds_powerup(bool on)
/* One-time RDS init at startup */
void si4700_rds_init(void)
{
- /* nothing to do */
+ semaphore_init(&rds_sema, 1, 0);
+ create_thread(rds_thread, rds_stack, sizeof(rds_stack), 0, "rds"
+ IF_PRIO(, PRIORITY_REALTIME) IF_COP(, CPU));
}
diff --git a/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c
index 13dff6ecb5..955301b4da 100644
--- a/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c
+++ b/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c
@@ -224,34 +224,6 @@ void pcm_play_dma_stop(void)
play_stop_pcm();
}
-/* Return a pointer to the samples and the number of them in *count */
-const void * pcm_play_dma_get_peak_buffer(int *count)
-{
- static unsigned long dsa NOCACHEBSS_ATTR;
- unsigned long addr;
- long offs, size;
- int oldstatus;
-
- /* read burst dma source address register in channel context */
- sdma_read_words(&dsa, CHANNEL_CONTEXT_ADDR(DMA_PLAY_CH_NUM)+0x0b, 1);
-
- oldstatus = disable_irq_save();
- addr = dsa;
- offs = addr - (unsigned long)dma_play_bd.buf_addr;
- size = dma_play_bd.mode.count;
- restore_irq(oldstatus);
-
- /* Be addresses are coherent (no buffer change during read) */
- if (offs >= 0 && offs < size)
- {
- *count = (size - offs) >> 2;
- return (void *)((addr + 2) & ~3);
- }
-
- *count = 0;
- return NULL;
-}
-
void * pcm_dma_addr(void *addr)
{
return (void *)addr_virt_to_phys((unsigned long)addr);
diff --git a/firmware/target/arm/imx31/gigabeat-s/system-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/system-gigabeat-s.c
index d7ebeea024..22873bfef0 100644
--- a/firmware/target/arm/imx31/gigabeat-s/system-gigabeat-s.c
+++ b/firmware/target/arm/imx31/gigabeat-s/system-gigabeat-s.c
@@ -199,7 +199,7 @@ void system_exception_wait(void)
system_halt();
}
-void INIT_ATTR system_init(void)
+void system_init(void)
{
static const enum IMX31_CG_LIST disable_clocks[] INITDATA_ATTR =
{
diff --git a/firmware/target/arm/imx31/i2c-imx31.c b/firmware/target/arm/imx31/i2c-imx31.c
index 972c7d465b..ce5e4cc8f8 100644
--- a/firmware/target/arm/imx31/i2c-imx31.c
+++ b/firmware/target/arm/imx31/i2c-imx31.c
@@ -367,7 +367,7 @@ int i2c_write(struct i2c_node *node, const unsigned char *data,
return -1;
}
-void INIT_ATTR i2c_init(void)
+void i2c_init(void)
{
/* Do one-time inits for each module that will be used - leave
* module disabled and unclocked until something wants it. */
diff --git a/firmware/target/arm/imx31/i2c-imx31.h b/firmware/target/arm/imx31/i2c-imx31.h
index b7305931d1..dad5a3da00 100644
--- a/firmware/target/arm/imx31/i2c-imx31.h
+++ b/firmware/target/arm/imx31/i2c-imx31.h
@@ -76,7 +76,7 @@ struct i2c_sync_transfer_desc
};
/* One-time init of i2c driver */
-void i2c_init(void);
+void i2c_init(void) INIT_ATTR;
/* Enable or disable the node - modules will be switched on/off accordingly. */
void i2c_enable_node(struct i2c_node *node, bool enable);
diff --git a/firmware/target/arm/ipod/backlight-mini1g_mini2g.c b/firmware/target/arm/ipod/backlight-mini1g_mini2g.c
index 8dd7ef87bd..143be32e91 100644
--- a/firmware/target/arm/ipod/backlight-mini1g_mini2g.c
+++ b/firmware/target/arm/ipod/backlight-mini1g_mini2g.c
@@ -43,3 +43,10 @@ void backlight_hw_off(void)
{
GPIO_CLEAR_BITWISE(GPIOB_OUTPUT_VAL, 0x08);
}
+
+bool backlight_hw_init(void)
+{
+ GPIO_SET_BITWISE(GPIOB_ENABLE, 0x0c); /* B02 and B03 enable */
+ GPIO_SET_BITWISE(GPIOB_OUTPUT_VAL, 0x08); /* B03 = 1 */
+ return true;
+}
diff --git a/firmware/target/arm/ipod/backlight-target.h b/firmware/target/arm/ipod/backlight-target.h
index 299a1cb6e7..133663d279 100644
--- a/firmware/target/arm/ipod/backlight-target.h
+++ b/firmware/target/arm/ipod/backlight-target.h
@@ -57,7 +57,7 @@ void backlight_hw_brightness(int val);
#elif defined(IPOD_MINI) || defined(IPOD_MINI2G)
-#define backlight_hw_init() true
+bool backlight_hw_init(void);
void backlight_hw_on(void);
void backlight_hw_off(void);
diff --git a/firmware/target/arm/ipod/button-target.h b/firmware/target/arm/ipod/button-target.h
index 82f600d302..75bc191608 100644
--- a/firmware/target/arm/ipod/button-target.h
+++ b/firmware/target/arm/ipod/button-target.h
@@ -47,15 +47,20 @@ void ipod_4g_button_int(void);
/* Remote control's buttons */
#ifdef IPOD_ACCESSORY_PROTOCOL
+#define BUTTON_RC_DOWN 0x01000000
+#define BUTTON_RC_UP 0x00800000
+#define BUTTON_RC_SELECT 0x00400000
+#define BUTTON_RC_MENU 0x00200000
#define BUTTON_RC_PLAY 0x00100000
#define BUTTON_RC_STOP 0x00080000
-
#define BUTTON_RC_LEFT 0x00040000
#define BUTTON_RC_RIGHT 0x00020000
#define BUTTON_RC_VOL_UP 0x00010000
#define BUTTON_RC_VOL_DOWN 0x00008000
-#define BUTTON_REMOTE (BUTTON_RC_PLAY|BUTTON_RC_STOP\
+#define BUTTON_REMOTE (BUTTON_RC_UP|BUTTON_RC_DOWN \
+ |BUTTON_RC_SELECT|BUTTON_RC_PLAY \
+ |BUTTON_RC_PLAY|BUTTON_RC_STOP \
|BUTTON_RC_LEFT|BUTTON_RC_RIGHT\
|BUTTON_RC_VOL_UP|BUTTON_RC_VOL_DOWN)
#endif
diff --git a/firmware/target/arm/ipod/lcd-gray.c b/firmware/target/arm/ipod/lcd-gray.c
index d8695cdb10..883897b997 100644
--- a/firmware/target/arm/ipod/lcd-gray.c
+++ b/firmware/target/arm/ipod/lcd-gray.c
@@ -333,17 +333,18 @@ void lcd_update_rect(int x, int y, int width, int height)
x >>= 3;
width = xmax - x + 1;
- for (; y <= ymax; y++)
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
+ for (; y <= ymax; y++)
{
lcd_cmd_and_data(R_RAM_ADDR_SET, (y << 5) + addr_offset - x);
lcd_prepare_cmd(R_RAM_DATA);
-
+ fb_data *data = fbaddr(2*x,y);
#if defined(IPOD_MINI) || defined(IPOD_MINI2G)
if (pix_offset == -2)
- lcd_write_data_shifted(FBADDR(2*x, y), width);
+ lcd_write_data_shifted(data, width);
else
#endif
- lcd_write_data(FBADDR(2*x, y), width);
+ lcd_write_data(data, width);
}
}
diff --git a/firmware/target/arm/ipod/video/lcd-as-video.S b/firmware/target/arm/ipod/video/lcd-as-video.S
index 47155b8c75..7d6caef448 100644
--- a/firmware/target/arm/ipod/video/lcd-as-video.S
+++ b/firmware/target/arm/ipod/video/lcd-as-video.S
@@ -40,24 +40,24 @@ lcd_write_data: /* r1 = pixel count, must be even */
subs r1, r1, #16
.loop16:
- ldmgeia r0!, {r2-r3}
- stmgeia lr, {r2-r3}
- ldmgeia r0!, {r2-r3}
- stmgeia lr, {r2-r3}
- ldmgeia r0!, {r2-r3}
- stmgeia lr, {r2-r3}
- ldmgeia r0!, {r2-r3}
- stmgeia lr, {r2-r3}
- subges r1, r1, #16
+ ldmiage r0!, {r2-r3}
+ stmiage lr, {r2-r3}
+ ldmiage r0!, {r2-r3}
+ stmiage lr, {r2-r3}
+ ldmiage r0!, {r2-r3}
+ stmiage lr, {r2-r3}
+ ldmiage r0!, {r2-r3}
+ stmiage lr, {r2-r3}
+ subsge r1, r1, #16
bge .loop16
/* no need to correct the count, we're just checking bits from now */
tst r1, #8
- ldmneia r0!, {r2-r4, r12}
- stmneia lr, {r2-r4, r12}
+ ldmiane r0!, {r2-r4, r12}
+ stmiane lr, {r2-r4, r12}
tst r1, #4
- ldmneia r0!, {r2-r3}
- stmneia lr, {r2-r3}
+ ldmiane r0!, {r2-r3}
+ stmiane lr, {r2-r3}
tst r1, #2
ldrne r3, [r0], #4
strne r3, [lr]
diff --git a/firmware/target/arm/lcd-ssd1815.c b/firmware/target/arm/lcd-ssd1815.c
index 891d4ef64d..0af20cd34f 100644
--- a/firmware/target/arm/lcd-ssd1815.c
+++ b/firmware/target/arm/lcd-ssd1815.c
@@ -144,81 +144,6 @@ void lcd_init_device(void)
lcd_write_command(LCD_SET_NORMAL_DISPLAY);
}
-
-#elif defined(CPU_TCC77X)
-
-/* TCC77x specific defines */
-#define LCD_BASE 0x50000000
-#define LCD_CMD *(volatile unsigned char*)(LCD_BASE)
-#define LCD_DATA *(volatile unsigned char*)(LCD_BASE+1)
-
-void lcd_write_command(int byte)
-{
- LCD_CMD = byte;
-
- asm volatile (
- "nop \n\t"
- "nop \n\t"
- "nop \n\t"
- );
-}
-
-void lcd_write_data(const fb_data* p_bytes, int count)
-{
- while (count--)
- {
- LCD_DATA = *(p_bytes++);
-
- asm volatile (
- "nop \n\t"
- "nop \n\t"
- "nop \n\t"
- );
- }
-}
-
-/* LCD init */
-void lcd_init_device(void)
-{
- uint32_t bus_width;
-
- /* Telechips init the same as the original firmware */
- CSCFG1 &= 0xc3ffc000;
- CSCFG1 |= 0x3400101a;
- CSCFG1 |= (1 << 21);
- CSCFG1 &= ~(1 << 21);
-
- bus_width = ((MCFG >> 11) & 0x3) ^ 3;
-
- CSCFG1 = (bus_width << 28) |
- (3 << 26) | /* MTYPE = 3 */
- ((LCD_BASE >> 28) << 22) | /* CSBASE = 0x5 */
- (1 << 20) | /* Unknown */
- (3 << 11) | /* Setup time = 3 cycles */
- (3 << 3) | /* Pulse width = 3+1 cycles */
- (1 << 0); /* Hold time = 1 cycle */
-
- /* SSD1815 inits like the original firmware */
- lcd_write_command(LCD_SET_DISPLAY_OFF);
- lcd_set_flip(false);
- lcd_write_command(LCD_SET_INTERNAL_REGULATOR_RESISTOR_RATIO | 5);
- lcd_set_contrast(lcd_default_contrast());
- lcd_write_command(LCD_SET_POWER_CONTROL_REGISTER | 7);
- /* power control register: op-amp=1, regulator=1, booster=1 */
- lcd_write_command(LCD_SET_BIAS_TC_OSC);
-
- /* 0xc2 = 110 000 10: Osc. Freq 110 - ???
- TC value 000 - "-0.01%/C (TC0, POR)"
- Bias ratio 10 - "1/9, 1/7 (POR)"
- */
- lcd_write_command(0xc2);
- lcd_write_command(LCD_SET_DISPLAY_ON);
-
- lcd_clear_display();
- lcd_update();
-}
-
-/* End of TCC77x specific defines */
#endif
@@ -296,6 +221,7 @@ void lcd_update(void)
{
int y;
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
/* Copy display bitmap to hardware */
for (y = 0; y < LCD_FBHEIGHT; y++)
{
@@ -303,7 +229,7 @@ void lcd_update(void)
lcd_write_command (LCD_CNTL_HIGHCOL | ((xoffset >> 4) & 0xf));
lcd_write_command (LCD_CNTL_LOWCOL | (xoffset & 0xf));
- lcd_write_data (FBADDR(0, y), LCD_WIDTH);
+ lcd_write_data (fbaddr(0,y), LCD_WIDTH);
}
}
@@ -324,6 +250,7 @@ void lcd_update_rect(int x, int y, int width, int height)
if(ymax >= LCD_FBHEIGHT)
ymax = LCD_FBHEIGHT-1;
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
/* Copy specified rectange bitmap to hardware */
for (; y <= ymax; y++)
{
@@ -331,6 +258,6 @@ void lcd_update_rect(int x, int y, int width, int height)
lcd_write_command (LCD_CNTL_HIGHCOL | (((x+xoffset) >> 4) & 0xf));
lcd_write_command (LCD_CNTL_LOWCOL | ((x+xoffset) & 0xf));
- lcd_write_data (FBADDR(x,y), width);
+ lcd_write_data (fbaddr(x,y), width);
}
}
diff --git a/firmware/target/arm/olympus/mrobe-100/lcd-mr100.c b/firmware/target/arm/olympus/mrobe-100/lcd-mr100.c
index d336ad7419..c10b4ca8f6 100644
--- a/firmware/target/arm/olympus/mrobe-100/lcd-mr100.c
+++ b/firmware/target/arm/olympus/mrobe-100/lcd-mr100.c
@@ -232,6 +232,7 @@ void lcd_update(void)
cmd1 = LCD_CNTL_HIGHCOL | (((xoffset) >> 4) & 0xf);
cmd2 = LCD_CNTL_LOWCOL | ((xoffset) & 0xf);
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
/* Copy display bitmap to hardware */
for (y = 0; y < LCD_FBHEIGHT; y++)
{
@@ -239,7 +240,7 @@ void lcd_update(void)
lcd_write_command(cmd1);
lcd_write_command(cmd2);
- lcd_write_data (FBADDR(0, y), LCD_WIDTH);
+ lcd_write_data (fbaddr(0,y), LCD_WIDTH);
}
}
@@ -264,6 +265,7 @@ void lcd_update_rect(int x, int y, int width, int height)
cmd1 = LCD_CNTL_HIGHCOL | (((x + xoffset) >> 4) & 0xf);
cmd2 = LCD_CNTL_LOWCOL | ((x + xoffset) & 0xf);
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
/* Copy specified rectange bitmap to hardware */
for (; y <= ymax; y++)
{
@@ -271,6 +273,6 @@ void lcd_update_rect(int x, int y, int width, int height)
lcd_write_command(cmd1);
lcd_write_command(cmd2);
- lcd_write_data (FBADDR(x,y), width);
+ lcd_write_data (fbaddr(x,y), width);
}
}
diff --git a/firmware/target/arm/pcm-telechips.c b/firmware/target/arm/pcm-telechips.c
index 68bae1b0a9..45044bc664 100644
--- a/firmware/target/arm/pcm-telechips.c
+++ b/firmware/target/arm/pcm-telechips.c
@@ -62,14 +62,6 @@ struct dma_data dma_play_data SHAREDBSS_ATTR =
.state = 0
};
-const void * pcm_play_dma_get_peak_buffer(int *count)
-{
- unsigned long addr = (unsigned long)dma_play_data.p;
- size_t cnt = dma_play_data.size;
- *count = cnt >> 2;
- return (void *)((addr + 2) & ~3);
-}
-
void pcm_play_dma_init(void)
{
DAVC = 0x0; /* Digital Volume = max */
@@ -82,19 +74,6 @@ void pcm_play_dma_init(void)
/* Enable DAI block in Master mode, 256fs->32fs, 16bit LSB */
DAMR = 0x3c8e80;
-#elif defined(IAUDIO_7)
- BCLKCTR &= ~DEV_DAI;
- PCLK_DAI = (0x800a << 16) | (PCLK_DAI & 0xffff);
- BCLKCTR |= DEV_DAI;
-
- /* Master mode, 256->64fs, 16bit LSB*/
- DAMR = 0x3cce20;
-#elif defined(LOGIK_DAX)
- /* TODO */
-#elif defined(SANSA_M200)
- /* TODO */
-#elif defined(SANSA_C100)
- /* TODO */
#else
#error "Target isn't supported"
#endif
@@ -228,7 +207,7 @@ const void * pcm_rec_dma_get_peak_buffer(void)
}
#endif
-#if defined(CPU_TCC77X) || defined(CPU_TCC780X)
+#if defined(CPU_TCC780X)
void fiq_handler(void) ICODE_ATTR __attribute__((naked));
void fiq_handler(void)
{
@@ -239,15 +218,13 @@ void fiq_handler(void)
* r0-r3 and r12 is a working register.
*/
asm volatile (
+ BEGIN_ARM_ASM_SYNTAX_UNIFIED
"sub lr, lr, #4 \n"
"stmfd sp!, { r0-r3, lr } \n" /* stack scratch regs and lr */
"mov r14, #0 \n" /* Was the callback called? */
#if defined(CPU_TCC780X)
"mov r8, #0xc000 \n" /* DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK */
"ldr r9, =0xf3001004 \n" /* CREQ */
-#elif defined(CPU_TCC77X)
- "mov r8, #0x0030 \n" /* DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK */
- "ldr r9, =0x80000104 \n" /* CREQ */
#endif
"str r8, [r9] \n" /* clear DAI IRQs */
"ldmia r11, { r8-r9 } \n" /* r8 = p, r9 = size */
@@ -275,7 +252,7 @@ void fiq_handler(void)
"stmia r11, { r8-r9 } \n" /* save p and size */
"cmp r14, #0 \n" /* Callback called? */
- "ldmeqfd sp!, { r0-r3, pc }^ \n" /* no? -> exit */
+ "ldmfdeq sp!, { r0-r3, pc }^ \n" /* no? -> exit */
"ldr r1, =pcm_play_status_callback \n"
"ldr r1, [r1] \n"
@@ -292,11 +269,12 @@ void fiq_handler(void)
"mov lr, pc \n"
"ldr pc, =pcm_play_dma_complete_callback \n"
"cmp r0, #0 \n" /* any more to play? */
- "ldmneia r11, { r8-r9 } \n" /* load new p and size */
+ "ldmiane r11, { r8-r9 } \n" /* load new p and size */
"cmpne r9, #0x0f \n" /* did we actually get enough data? */
"bhi .fill_fifo \n" /* not stop and enough? refill */
"ldmfd sp!, { r0-r3, pc }^ \n" /* exit */
".ltorg \n"
+ END_ARM_ASM_SYNTAX_UNIFIED
: : "i"(PCM_DMAST_OK), "i"(PCM_DMAST_STARTED)
);
}
diff --git a/firmware/target/arm/pnx0101/app.lds b/firmware/target/arm/pnx0101/app.lds
deleted file mode 100644
index f14ef90129..0000000000
--- a/firmware/target/arm/pnx0101/app.lds
+++ /dev/null
@@ -1,144 +0,0 @@
-#include "config.h"
-
-ENTRY(start)
-
-OUTPUT_FORMAT(elf32-littlearm)
-OUTPUT_ARCH(arm)
-STARTUP(target/arm/pnx0101/crt0-pnx0101.o)
-
-#define PLUGINSIZE PLUGIN_BUFFER_SIZE
-#define CODECSIZE CODEC_SIZE
-
-#ifdef DEBUG
-#define STUBOFFSET 0x10000
-#else
-#define STUBOFFSET 0
-#endif
-
-#define DRAMSIZE (MEMORYSIZE * 0x100000) - PLUGINSIZE - STUBOFFSET - CODECSIZE
-
-#define DRAMORIG 0xc00000 + STUBOFFSET
-#define IRAM0ORIG 0x000000
-#define IRAM0SIZE 0x7000
-#define IRAMORIG 0x400000
-#define IRAMSIZE 0x7000
-
-/* End of the audio buffer, where the codec buffer starts */
-#define ENDAUDIOADDR (DRAMORIG + DRAMSIZE)
-
-/* Where the codec buffer ends, and the plugin buffer starts */
-#define ENDADDR (ENDAUDIOADDR + CODECSIZE)
-
-MEMORY
-{
- DRAM : ORIGIN = DRAMORIG, LENGTH = DRAMSIZE
- IRAM : ORIGIN = IRAMORIG, LENGTH = IRAMSIZE
- IRAM0 : ORIGIN = IRAM0ORIG, LENGTH = IRAM0SIZE
-}
-
-SECTIONS
-{
- .text :
- {
- loadaddress = .;
- _loadaddress = .;
- . = ALIGN(0x200);
- *(.init.text)
- *(.text*)
- *(.glue_7)
- *(.glue_7t)
- . = ALIGN(0x4);
- } > DRAM
-
- .rodata :
- {
- *(.rodata) /* problems without this, dunno why */
- *(.rodata*)
- *(.rodata.str1.1)
- *(.rodata.str1.4)
- . = ALIGN(0x4);
- } > DRAM
-
- .data :
- {
- *(.data*)
- . = ALIGN(0x4);
- } > DRAM
-
- /DISCARD/ :
- {
- *(.eh_frame)
- }
-
- .vectors 0x0 :
- {
- _vectorsstart = .;
- KEEP(*(.vectors));
- _vectorsend = .;
- *(.dmabuf)
- } >IRAM0 AT> DRAM
-
- _vectorscopy = LOADADDR(.vectors);
-
- .iram IRAMORIG + SIZEOF(.vectors) :
- {
- _iramstart = .;
- *(.icode)
- *(.irodata)
- *(.idata)
- . = ALIGN(0x4);
- _iramend = .;
- } > IRAM AT> DRAM
-
- _iramcopy = LOADADDR(.iram);
-
- .ibss (NOLOAD) :
- {
- _iedata = .;
- *(.ibss)
- . = ALIGN(0x4);
- _iend = .;
- } > IRAM
-
- .stack :
- {
- *(.stack)
- stackbegin = .;
- . += 0x2000;
- stackend = .;
- } > IRAM
-
- .bss ADDR(.data) + SIZEOF(.data) + SIZEOF(.iram) + SIZEOF(.vectors):
- {
- _edata = .;
- *(.bss*)
- *(COMMON)
- . = ALIGN(0x4);
- _end = .;
- } > DRAM
-
- .audiobuf ALIGN(4) :
- {
- _audiobuffer = .;
- audiobuffer = .;
- } > DRAM
-
- .audiobufend ENDAUDIOADDR:
- {
- audiobufend = .;
- _audiobufend = .;
- } > DRAM
-
- .codec ENDAUDIOADDR:
- {
- codecbuf = .;
- _codecbuf = .;
- }
-
- .plugin ENDADDR:
- {
- _pluginbuf = .;
- pluginbuf = .;
- }
-}
-
diff --git a/firmware/target/arm/pnx0101/backlight-target.h b/firmware/target/arm/pnx0101/backlight-target.h
deleted file mode 100644
index 7ebe85d66f..0000000000
--- a/firmware/target/arm/pnx0101/backlight-target.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id: backlight-target.h 13136 2007-04-12 22:12:13Z amiconn $
- *
- * Copyright (C) 2006 by Barry Wardell
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ****************************************************************************/
-#ifndef BACKLIGHT_TARGET_H
-#define BACKLIGHT_TARGET_H
-
-#define backlight_hw_init() true
-void backlight_hw_on(void);
-void backlight_hw_off(void);
-
-#endif
diff --git a/firmware/target/arm/pnx0101/crt0-pnx0101.S b/firmware/target/arm/pnx0101/crt0-pnx0101.S
deleted file mode 100644
index 51be8c72a3..0000000000
--- a/firmware/target/arm/pnx0101/crt0-pnx0101.S
+++ /dev/null
@@ -1,225 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id: crt0.S 11850 2006-12-29 02:49:12Z markun $
- *
- * Copyright (C) 2002 by Linus Nielsen Feltzing
- *
- * 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.
- *
- ****************************************************************************/
-#define ASM /* do not include structure definitions from pnx0101.h */
-
-#include "config.h"
-#include "cpu.h"
-
- .section .init.text,"ax",%progbits
-
- .global start
-start:
-
-/* Arm bootloader and startup code based on startup.s from the iPodLinux loader
- *
- * Copyright (c) 2003, Daniel Palffy (dpalffy (at) rainstorm.org)
- * Copyright (c) 2005, Bernard Leach <leachbj@bouncycastle.org>
- *
- */
-
- msr cpsr_c, #0xd3 /* enter supervisor mode, disable IRQ */
-
-#ifndef BOOTLOADER
-#ifndef DEBUG
- ldr r0, =0x80105000
- mov r1, #1
- str r1, [r0, #4]
- mov r1, #0
- str r1, [r0, #4]
-1: ldr r1, [r0]
- cmp r1, #0
- bne 1b
- mov r1, #0x74
- str r1, [r0, #8]
- mov r1, #2
- str r1, [r0, #0x18]
- mov r1, #2
- str r1, [r0, #0x20]
- mov r1, #82
- str r1, [r0, #0x28]
- mov r1, #100
- str r1, [r0, #0x2c]
- mov r1, #0x120
- str r1, [r0, #0x30]
- mov r1, #6
- str r1, [r0, #4]
- ldr r0, =1f
- mov r15, r0
-1:
-#endif /* !DEBUG */
-
-#ifndef DEBUG
- /* Copy exception handler code to address 0 */
- ldr r2, =_vectorsstart
- ldr r3, =_vectorsend
- ldr r4, =_vectorscopy
-1:
- cmp r3, r2
- ldrhi r5, [r4], #4
- strhi r5, [r2], #4
- bhi 1b
-#else
- ldr r1, =vectors
- ldr r0, =irq_handler
- str r0, [r1, #24]
- ldr r0, =fiq_handler
- str r0, [r1, #28]
-#endif
-
-#ifndef STUB
- /* Zero out IBSS */
- ldr r2, =_iedata
- ldr r3, =_iend
- mov r4, #0
-1:
- cmp r3, r2
- strhi r4, [r2], #4
- bhi 1b
-
- /* Copy the IRAM */
- ldr r2, =_iramcopy
- ldr r3, =_iramstart
- ldr r4, =_iramend
-1:
- cmp r4, r3
- ldrhi r5, [r2], #4
- strhi r5, [r3], #4
- bhi 1b
-#endif /* !STUB */
-#endif /* !BOOTLOADER */
-
- /* Initialise bss section to zero */
- ldr r2, =_edata
- ldr r3, =_end
- mov r4, #0
-1:
- cmp r3, r2
- strhi r4, [r2], #4
- bhi 1b
-
- /* Set up stack for IRQ mode */
- msr cpsr_c, #0xd2
- ldr sp, =irq_stack
- /* Set up stack for FIQ mode */
- msr cpsr_c, #0xd1
- ldr sp, =fiq_stack
-
- /* Let svc, abort and undefined modes use irq stack */
- msr cpsr_c, #0xd3
- ldr sp =irq_stack
- msr cpsr_c, #0xd7
- ldr sp, =irq_stack
- msr cpsr_c, #0xdb
- ldr sp, =irq_stack
-
- /* Switch to sys mode */
- msr cpsr_c, #0xdf
-
- /* Set up some stack and munge it with 0xdeadbeef */
- ldr sp, =stackend
- mov r3, sp
- ldr r2, =stackbegin
- ldr r4, =0xdeadbeef
-1:
- cmp r3, r2
- strhi r4, [r2], #4
- bhi 1b
-
- bl main
- /* main() should never return */
-
-/* Exception handlers. Will be copied to address 0 after memory remapping */
- .section .vectors,"aw"
- ldr pc, [pc, #24]
- ldr pc, [pc, #24]
- ldr pc, [pc, #24]
- ldr pc, [pc, #24]
- ldr pc, [pc, #24]
- ldr pc, [pc, #24]
- ldr pc, [pc, #24]
- ldr pc, [pc, #24]
-
- /* Exception vectors */
- .global vectors
-vectors:
- .word start
- .word undef_instr_handler
- .word software_int_handler
- .word prefetch_abort_handler
- .word data_abort_handler
- .word reserved_handler
- .word irq_handler
- .word fiq_handler
-
- .text
-
-/* All illegal exceptions call into UIE with exception address as first
- parameter. This is calculated differently depending on which exception
- we're in. Second parameter is exception number, used for a string lookup
- in UIE.
- */
-undef_instr_handler:
- sub r0, lr, #4
- mov r1, #0
- b UIE
-
-/* We run sys mode most of the time, and should never see a software
- exception being thrown. Make it illegal and call UIE.
- */
-software_int_handler:
-reserved_handler:
- sub r0, lr, #4
- mov r1, #4
- b UIE
-
-prefetch_abort_handler:
- sub r0, lr, #4
- mov r1, #1
- b UIE
-
-fiq_handler:
- @ Branch straight to FIQ handler in pcm_playback.c. This also handles the
- @ the correct return sequence.
- stmfd sp!, {r0-r7, r12, lr}
- bl fiq
- ldmfd sp!, {r0-r7, r12, lr}
- subs pc, lr, #4
-
-irq_handler:
-#ifndef STUB
- stmfd sp!, {r0-r11, r12, lr}
- bl irq
- ldmfd sp!, {r0-r11, r12, lr}
-#endif
- subs pc, lr, #4
-
-#ifdef STUB
-UIE:
- b UIE
-#endif
-
-/* 256 words of IRQ stack */
- .space 256*4
-irq_stack:
-
-/* 256 words of FIQ stack */
- .space 256*4
-fiq_stack:
diff --git a/firmware/target/arm/pnx0101/debug-pnx0101.c b/firmware/target/arm/pnx0101/debug-pnx0101.c
deleted file mode 100644
index 8dae2e4925..0000000000
--- a/firmware/target/arm/pnx0101/debug-pnx0101.c
+++ /dev/null
@@ -1,5 +0,0 @@
-#include <stdbool.h>
-bool dbg_ports()
-{
- return false;
-}
diff --git a/firmware/target/arm/pnx0101/iriver-ifp7xx/button-ifp7xx.c b/firmware/target/arm/pnx0101/iriver-ifp7xx/button-ifp7xx.c
deleted file mode 100644
index 45f9fae478..0000000000
--- a/firmware/target/arm/pnx0101/iriver-ifp7xx/button-ifp7xx.c
+++ /dev/null
@@ -1,90 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2006 by Barry Wardell
- *
- * 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 <stdlib.h>
-#include "config.h"
-#include "cpu.h"
-#include "system.h"
-#include "button.h"
-#include "kernel.h"
-#include "backlight.h"
-#include "adc.h"
-
-
-void button_init_device(void)
-{
-
-}
-
-bool button_hold(void)
-{
- return (GPIO5_READ & 4) ? false : true;
-}
-
-/*
- * Get button pressed from hardware
- */
-int button_read_device(void)
-{
- int btn = BUTTON_NONE;
- int data;
- static bool hold_button = false;
- bool hold_button_old;
-
- /* normal buttons */
- hold_button_old = hold_button;
- hold_button = button_hold();
-
- if (hold_button != hold_button_old)
- backlight_hold_changed(hold_button);
-
- if (!button_hold())
- {
- data = adc_read(ADC_BUTTONS);
- if (data < 0x35c)
- {
- if (data < 0x151)
- if (data < 0xc7)
- if (data < 0x41)
- btn = BUTTON_LEFT;
- else
- btn = BUTTON_RIGHT;
- else
- btn = BUTTON_SELECT;
- else
- if (data < 0x268)
- if (data < 0x1d7)
- btn = BUTTON_UP;
- else
- btn = BUTTON_DOWN;
- else
- if (data < 0x2f9)
- btn = BUTTON_EQ;
- else
- btn = BUTTON_MODE;
- }
-
- if (adc_read(ADC_BUTTON_PLAY) < 0x64)
- btn |= BUTTON_PLAY;
- }
- return btn;
-}
diff --git a/firmware/target/arm/pnx0101/iriver-ifp7xx/button-target.h b/firmware/target/arm/pnx0101/iriver-ifp7xx/button-target.h
deleted file mode 100644
index 6932b8956f..0000000000
--- a/firmware/target/arm/pnx0101/iriver-ifp7xx/button-target.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2006 by Barry Wardell
- *
- * 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.
- *
- ****************************************************************************/
-
-/* Custom written for the ifp7xx */
-
-#ifndef _BUTTON_TARGET_H_
-#define _BUTTON_TARGET_H_
-
-#define HAS_BUTTON_HOLD
-
-/* iriver IFP7XX specific button codes */
-
-#define BUTTON_PLAY 0x00000001
-#define BUTTON_SELECT 0x00000002
-
-#define BUTTON_LEFT 0x00000004
-#define BUTTON_RIGHT 0x00000008
-#define BUTTON_UP 0x00000010
-#define BUTTON_DOWN 0x00000020
-
-#define BUTTON_MODE 0x00000040
-#define BUTTON_EQ 0x00000080
-
-#define BUTTON_MAIN (BUTTON_PLAY|BUTTON_SELECT\
- |BUTTON_LEFT|BUTTON_RIGHT|BUTTON_UP|BUTTON_DOWN\
- |BUTTON_MODE|BUTTON_EQ)
-
-#define POWEROFF_BUTTON BUTTON_PLAY
-#define POWEROFF_COUNT 40
-
-#endif /* _BUTTON_TARGET_H_ */
diff --git a/firmware/target/arm/pnx0101/iriver-ifp7xx/lcd-ifp7xx.c b/firmware/target/arm/pnx0101/iriver-ifp7xx/lcd-ifp7xx.c
deleted file mode 100644
index 1ad604d50c..0000000000
--- a/firmware/target/arm/pnx0101/iriver-ifp7xx/lcd-ifp7xx.c
+++ /dev/null
@@ -1,224 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2002 by Alan Korr
- *
- * 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 "lcd.h"
-#include "system.h"
-
-/*** definitions ***/
-
-#define LCD_SET_LOWER_COLUMN_ADDRESS ((char)0x00)
-#define LCD_SET_HIGHER_COLUMN_ADDRESS ((char)0x10)
-#define LCD_SET_INTERNAL_REGULATOR_RESISTOR_RATIO ((char)0x20)
-#define LCD_SET_POWER_CONTROL_REGISTER ((char)0x28)
-#define LCD_SET_DISPLAY_START_LINE ((char)0x40)
-#define LCD_SET_CONTRAST_CONTROL_REGISTER ((char)0x81)
-#define LCD_SET_SEGMENT_REMAP ((char)0xA0)
-#define LCD_SET_LCD_BIAS ((char)0xA2)
-#define LCD_SET_ENTIRE_DISPLAY_OFF ((char)0xA4)
-#define LCD_SET_ENTIRE_DISPLAY_ON ((char)0xA5)
-#define LCD_SET_NORMAL_DISPLAY ((char)0xA6)
-#define LCD_SET_REVERSE_DISPLAY ((char)0xA7)
-#define LCD_SET_MULTIPLEX_RATIO ((char)0xA8)
-#define LCD_SET_BIAS_TC_OSC ((char)0xA9)
-#define LCD_SET_1OVER4_BIAS_RATIO ((char)0xAA)
-#define LCD_SET_INDICATOR_OFF ((char)0xAC)
-#define LCD_SET_INDICATOR_ON ((char)0xAD)
-#define LCD_SET_DISPLAY_OFF ((char)0xAE)
-#define LCD_SET_DISPLAY_ON ((char)0xAF)
-#define LCD_SET_PAGE_ADDRESS ((char)0xB0)
-#define LCD_SET_COM_OUTPUT_SCAN_DIRECTION ((char)0xC0)
-#define LCD_SET_TOTAL_FRAME_PHASES ((char)0xD2)
-#define LCD_SET_DISPLAY_OFFSET ((char)0xD3)
-#define LCD_SET_READ_MODIFY_WRITE_MODE ((char)0xE0)
-#define LCD_SOFTWARE_RESET ((char)0xE2)
-#define LCD_NOP ((char)0xE3)
-#define LCD_SET_END_OF_READ_MODIFY_WRITE_MODE ((char)0xEE)
-
-/* LCD command codes */
-#define LCD_CNTL_RESET 0xe2 /* Software reset */
-#define LCD_CNTL_POWER 0x2f /* Power control */
-#define LCD_CNTL_CONTRAST 0x81 /* Contrast */
-#define LCD_CNTL_OUTSCAN 0xc8 /* Output scan direction */
-#define LCD_CNTL_SEGREMAP 0xa1 /* Segment remap */
-#define LCD_CNTL_DISPON 0xaf /* Display on */
-
-#define LCD_CNTL_PAGE 0xb0 /* Page address */
-#define LCD_CNTL_HIGHCOL 0x10 /* Upper column address */
-#define LCD_CNTL_LOWCOL 0x00 /* Lower column address */
-
-/*** driver routines ***/
-
-void lcd_write_command(int cmd)
-{
- while ((LCDSTAT & 3) != 3);
- LCDCMD = cmd;
-}
-
-void lcd_write_data( const unsigned char* data, int count )
-{
- int i;
- for (i=0; i < count; i++) {
- while ((LCDSTAT & 3) != 3);
- LCDDATA = data[i];
- }
-}
-
-/*** hardware configuration ***/
-
-int lcd_default_contrast(void)
-{
- return 45;
-}
-
-void lcd_set_contrast(int val)
-{
- lcd_write_command(LCD_CNTL_CONTRAST);
- lcd_write_command(val);
-}
-
-void lcd_set_invert_display(bool yesno)
-{
- if (yesno)
- lcd_write_command(LCD_SET_REVERSE_DISPLAY);
- else
- lcd_write_command(LCD_SET_NORMAL_DISPLAY);
-}
-
-/* turn the display upside down (call lcd_update() afterwards) */
-void lcd_set_flip(bool yesno)
-{
- if (yesno)
- {
- lcd_write_command(LCD_SET_SEGMENT_REMAP);
- lcd_write_command(LCD_SET_COM_OUTPUT_SCAN_DIRECTION);
- }
- else
- {
- lcd_write_command(LCD_SET_SEGMENT_REMAP | 0x01);
- lcd_write_command(LCD_SET_COM_OUTPUT_SCAN_DIRECTION | 0x08);
- }
-}
-
-void lcd_init_device(void)
-{
- LCDREG10 = 0xf;
- LCDREG04 = 0x4084;
-
- /* inits like the original firmware */
- lcd_write_command(LCD_SOFTWARE_RESET);
- lcd_write_command(LCD_SET_INTERNAL_REGULATOR_RESISTOR_RATIO + 4);
- lcd_write_command(LCD_SET_LCD_BIAS);
- lcd_write_command(LCD_SET_POWER_CONTROL_REGISTER + 7);
- /* power control register: op-amp=1, regulator=1, booster=1 */
- lcd_write_command(LCD_SET_DISPLAY_ON);
- lcd_write_command(LCD_SET_NORMAL_DISPLAY);
- lcd_set_flip(false);
- lcd_write_command(LCD_SET_DISPLAY_START_LINE + 0);
- lcd_set_contrast(lcd_default_contrast());
- lcd_write_command(LCD_SET_PAGE_ADDRESS);
- lcd_write_command(LCD_SET_LOWER_COLUMN_ADDRESS + 0);
- lcd_write_command(LCD_SET_HIGHER_COLUMN_ADDRESS + 0);
-
- lcd_clear_display();
- lcd_update();
-}
-
-/*** Update functions ***/
-
-/* Performance function that works with an external buffer
- note that by and bheight are in 8-pixel units! */
-void lcd_blit_mono(const unsigned char *data, int x, int by, int width,
- int bheight, int stride)
-{
- /* Copy display bitmap to hardware */
- while (bheight--)
- {
- lcd_write_command (LCD_CNTL_PAGE | (by++ & 0xf));
- lcd_write_command (LCD_CNTL_HIGHCOL | (((x+4)>>4) & 0xf));
- lcd_write_command (LCD_CNTL_LOWCOL | ((x+4) & 0xf));
-
- lcd_write_data(data, width);
- data += stride;
- }
-}
-
-
-/* Performance function that works with an external buffer
- note that by and bheight are in 8-pixel units! */
-void lcd_blit_grey_phase(unsigned char *values, unsigned char *phases,
- int x, int by, int width, int bheight, int stride)
-{
- (void)values;
- (void)phases;
- (void)x;
- (void)by;
- (void)width;
- (void)bheight;
- (void)stride;
-}
-
-/* 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)
-{
- int y;
-
- /* Copy display bitmap to hardware */
- for (y = 0; y < LCD_FBHEIGHT; y++)
- {
- lcd_write_command (LCD_CNTL_PAGE | (y & 0xf));
- lcd_write_command (LCD_CNTL_HIGHCOL);
- lcd_write_command (LCD_CNTL_LOWCOL | 4);
-
- lcd_write_data (FBADDR(0, y), LCD_WIDTH);
- }
-}
-
-/* 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 ymax;
-
- /* The Y coordinates have to work on even 8 pixel rows */
- ymax = (y + height-1) >> 3;
- y >>= 3;
-
- if(x + width > LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (width <= 0)
- return; /* nothing left to do, 0 is harmful to lcd_write_data() */
- if(ymax >= LCD_FBHEIGHT)
- ymax = LCD_FBHEIGHT-1;
-
- /* Copy specified rectange bitmap to hardware */
- for (; y <= ymax; y++)
- {
- lcd_write_command (LCD_CNTL_PAGE | (y & 0xf));
- lcd_write_command (LCD_CNTL_HIGHCOL | (((x+4) >> 4) & 0xf));
- lcd_write_command (LCD_CNTL_LOWCOL | ((x+4) & 0xf));
-
- lcd_write_data (FBADDR(x,y), width);
- }
-}
diff --git a/firmware/target/arm/pnx0101/iriver-ifp7xx/powermgmt-ifp7xx.c b/firmware/target/arm/pnx0101/iriver-ifp7xx/powermgmt-ifp7xx.c
deleted file mode 100644
index 4ce90a4c4f..0000000000
--- a/firmware/target/arm/pnx0101/iriver-ifp7xx/powermgmt-ifp7xx.c
+++ /dev/null
@@ -1,55 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2002 by Heikki Hannikainen, Uwe Freese
- * Revisions copyright (C) 2005 by Gerald Van Baren
- *
- * 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 "adc.h"
-#include "powermgmt.h"
-
-const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] =
-{
- 1050, 1150
-};
-
-const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] =
-{
- 1050, 1150 /* FIXME: just copied from above, was missing in powermgmt.c */
-};
-
-/* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */
-const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11] =
-{
- /* These values are the same as for Ondio divided by 3. */
- /* May need recalibration. */
- { 930, 1080, 1140, 1180, 1210, 1250, 1280, 1320, 1360, 1420, 1580 }, /* alkaline */
- { 1030, 1180, 1210, 1230, 1240, 1250, 1260, 1270, 1280, 1290, 1350 } /* NiMH */
-};
-
-/* TODO: only roughly correct */
-#define BATTERY_SCALE_FACTOR 3072
-/* full-scale ADC readout (2^10) in millivolt */
-
-/* Returns battery voltage from ADC [millivolts] */
-int _battery_voltage(void)
-{
- return (adc_read(ADC_UNREG_POWER) * BATTERY_SCALE_FACTOR) >> 10;
-}
-
diff --git a/firmware/target/arm/pnx0101/iriver-ifp7xx/system-target.h b/firmware/target/arm/pnx0101/iriver-ifp7xx/system-target.h
deleted file mode 100644
index 9aad4a7b74..0000000000
--- a/firmware/target/arm/pnx0101/iriver-ifp7xx/system-target.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2002 by Alan Korr
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ****************************************************************************/
-#ifndef SYSTEM_TARGET_H
-#define SYSTEM_TARGET_H
-
-#include "system-arm.h"
-
-#define CPUFREQ_DEFAULT 12000000
-#define CPUFREQ_NORMAL 48000000
-#define CPUFREQ_MAX 60000000
-
-typedef void (*interrupt_handler_t)(void);
-
-void irq_set_int_handler(int n, interrupt_handler_t handler);
-void irq_enable_int(int n);
-void irq_disable_int(int n);
-
-#endif /* SYSTEM_TARGET_H */
diff --git a/firmware/target/arm/pnx0101/kernel-pnx0101.c b/firmware/target/arm/pnx0101/kernel-pnx0101.c
deleted file mode 100644
index db0f1b0d7f..0000000000
--- a/firmware/target/arm/pnx0101/kernel-pnx0101.c
+++ /dev/null
@@ -1,45 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2007 by Tomasz Malesinski
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ****************************************************************************/
-#include "config.h"
-#include "system.h"
-#include "kernel.h"
-
-void timer_handler(void)
-{
- /* Run through the list of tick tasks */
- call_tick_tasks();
-
- TIMER0.clr = 0;
-}
-
-void tick_start(unsigned int interval_in_ms)
-{
- TIMER0.ctrl &= ~0x80; /* Disable the counter */
- TIMER0.ctrl |= 0x40; /* Reload after counting down to zero */
- TIMER0.load = 3000000 * interval_in_ms / 1000;
- TIMER0.ctrl &= ~0xc; /* No prescaler */
- TIMER0.clr = 1; /* Clear the interrupt request */
-
- irq_set_int_handler(IRQ_TIMER0, timer_handler);
- irq_enable_int(IRQ_TIMER0);
-
- TIMER0.ctrl |= 0x80; /* Enable the counter */
-}
diff --git a/firmware/target/arm/pnx0101/pcm-pnx0101.c b/firmware/target/arm/pnx0101/pcm-pnx0101.c
deleted file mode 100644
index 6099dcb7ef..0000000000
--- a/firmware/target/arm/pnx0101/pcm-pnx0101.c
+++ /dev/null
@@ -1,207 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2006 by Tomek Malesinski
- *
- * 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 "audio.h"
-#include "string.h"
-#include "pcm-internal.h"
-
-#define DMA_BUF_SAMPLES 0x100
-
-short __attribute__((section(".dmabuf"))) dma_buf_left[DMA_BUF_SAMPLES];
-short __attribute__((section(".dmabuf"))) dma_buf_right[DMA_BUF_SAMPLES];
-
-const int16_t* p IBSS_ATTR;
-size_t p_size IBSS_ATTR;
-
-void pcm_play_lock(void)
-{
-}
-
-void pcm_play_unlock(void)
-{
-}
-
-void pcm_play_dma_start(const void *addr, size_t size)
-{
- p = addr;
- p_size = size;
-}
-
-void pcm_play_dma_stop(void)
-{
-}
-
-static inline void fill_dma_buf(int offset)
-{
- short *l, *r, *lend;
-
- l = dma_buf_left + offset;
- lend = l + DMA_BUF_SAMPLES / 2;
- r = dma_buf_right + offset;
-
- if (pcm_playing)
- {
- bool new_buffer =false;
-
- do
- {
- int count;
- const int16_t *tmp_p;
- count = MIN(p_size / 4, (size_t)(lend - l));
- tmp_p = p;
- p_size -= count * 4;
-
- if ((int)l & 3)
- {
- *l++ = *tmp_p++;
- *r++ = *tmp_p++;
- count--;
- }
- while (count >= 4)
- {
- asm("ldmia %0!, {r0, r1, r2, r3}\n\t"
- "and r4, r0, %3\n\t"
- "orr r4, r4, r1, lsl #16\n\t"
- "and r5, r2, %3\n\t"
- "orr r5, r5, r3, lsl #16\n\t"
- "stmia %1!, {r4, r5}\n\t"
- "bic r4, r1, %3\n\t"
- "orr r4, r4, r0, lsr #16\n\t"
- "bic r5, r3, %3\n\t"
- "orr r5, r5, r2, lsr #16\n\t"
- "stmia %2!, {r4, r5}"
- : "+r" (tmp_p), "+r" (l), "+r" (r)
- : "r" (0xffff)
- : "r0", "r1", "r2", "r3", "r4", "r5", "memory");
- count -= 4;
- }
- while (count > 0)
- {
- *l++ = *tmp_p++;
- *r++ = *tmp_p++;
- count--;
- }
- p = tmp_p;
-
- if (new_buffer)
- {
- new_buffer = false;
- pcm_play_dma_status_callback(PCM_DMAST_STARTED);
- }
-
- if (l >= lend)
- return;
-
- new_buffer = pcm_play_dma_complete_callback(PCM_DMAST_OK,
- &p, &p_size);
- }
- while (p_size);
- }
-
- if (l < lend)
- {
- memset(l, 0, sizeof(short) * (lend - l));
- memset(r, 0, sizeof(short) * (lend - l));
- }
-}
-
-static void audio_irq(void)
-{
- unsigned long st = DMAINTSTAT & ~DMAINTEN;
- int i;
- for (i = 0; i < 2; i++)
- if (st & (1 << i))
- {
- fill_dma_buf((i == 1) ? 0 : DMA_BUF_SAMPLES / 2);
- DMAINTSTAT = 1 << i;
- }
-}
-
-unsigned long physical_address(void *p)
-{
- unsigned long adr = (unsigned long)p;
- return (MMUBLOCK((adr >> 21) & 0xf) << 21) | (adr & ((1 << 21) - 1));
-}
-
-void pcm_init(void)
-{
- int i;
-
- memset(dma_buf_left, 0, sizeof(dma_buf_left));
- memset(dma_buf_right, 0, sizeof(dma_buf_right));
-
- for (i = 0; i < 8; i++)
- {
- DMASRC(i) = 0;
- DMADEST(i) = 0;
- DMALEN(i) = 0x1ffff;
- DMAR0C(i) = 0;
- DMAR10(i) = 0;
- DMAR1C(i) = 0;
- }
-
- DMAINTSTAT = 0xc000ffff;
- DMAINTEN = 0xc000ffff;
-
- DMASRC(0) = physical_address(dma_buf_left);
- DMADEST(0) = 0x80200280;
- DMALEN(0) = 0xff;
- DMAR1C(0) = 0;
- DMAR0C(0) = 0x40408;
-
- DMASRC(1) = physical_address(dma_buf_right);
- DMADEST(1) = 0x80200284;
- DMALEN(1) = 0xff;
- DMAR1C(1) = 0;
- DMAR0C(1) = 0x40409;
-
- irq_set_int_handler(0x1b, audio_irq);
- irq_enable_int(0x1b);
-
- DMAINTSTAT = 1;
- DMAINTSTAT = 2;
- DMAINTEN &= ~3;
- DMAR10(0) |= 1;
- DMAR10(1) |= 1;
-}
-
-void pcm_play_dma_postinit(void)
-{
- audiohw_postinit();
-}
-
-void pcm_dma_apply_settings(void)
-{
-}
-
-const void * pcm_play_dma_get_peak_buffer(int *count)
-{
- unsigned long addr = (unsigned long)p;
- size_t cnt = p_size;
- *count = cnt >> 2;
- return (void *)((addr + 2) & ~3);
-}
-
-void audiohw_set_volume(int value)
-{
- int tmp = (60 - value * 4) & 0xff;
- CODECVOL = tmp | (tmp << 8);
-}
diff --git a/firmware/target/arm/pnx0101/system-pnx0101.c b/firmware/target/arm/pnx0101/system-pnx0101.c
deleted file mode 100644
index 63720d11be..0000000000
--- a/firmware/target/arm/pnx0101/system-pnx0101.c
+++ /dev/null
@@ -1,317 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id: $
- *
- * Copyright (C) 2007 by Tomasz Malesinski
- *
- * 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 <stdlib.h>
-#include "pnx0101.h"
-#include "system.h"
-
-static struct
-{
- unsigned char freq;
- unsigned char sys_mult;
- unsigned char sys_div;
-}
-perf_modes[3] ICONST_ATTR =
-{
- {12, 4, 4},
- {48, 4, 1},
- {60, 5, 1}
-};
-
-static int performance_mode, bus_divider;
-
-static void cgu_set_sel_stage_input(int clock, int input)
-{
- int s = CGU.base_ssr[clock];
- if (s & 1)
- CGU.base_fs2[clock] = input;
- else
- CGU.base_fs1[clock] = input;
- CGU.base_scr[clock] = (s & 3) ^ 3;
-}
-
-static void cgu_reset_sel_stage_clocks(int first_esr, int n_esr,
- int first_div, int n_div)
-{
- int i;
- for (i = 0; i < n_esr; i++)
- CGU.clk_esr[first_esr + i] = 0;
- for (i = 0; i < n_div; i++)
- CGU.base_fdc[first_div + i] = 0;
-}
-
-static void cgu_configure_div(int div, int n, int m)
-{
- int msub, madd, div_size, max_n;
- unsigned long cfg;
-
- if (n == m)
- {
- CGU.base_fdc[div] = CGU.base_fdc[div] & ~1;
- return;
- }
-
- msub = -n;
- madd = m - n;
- div_size = (div == PNX0101_HIPREC_FDC) ? 10 : 8;
- max_n = 1 << div_size;
- while ((madd << 1) < max_n && (msub << 1) >= -max_n)
- {
- madd <<= 1;
- msub <<= 1;
- }
- cfg = (((msub << div_size) | madd) << 3) | 4;
- CGU.base_fdc[div] = CGU.base_fdc[div] & ~1;
- CGU.base_fdc[div] = cfg | 2;
- CGU.base_fdc[div] = cfg;
- CGU.base_fdc[div] = cfg | 1;
-}
-
-static void cgu_connect_div_to_clock(int rel_div, int esr)
-{
- CGU.clk_esr[esr] = (rel_div << 1) | 1;
-}
-
-static void cgu_enable_clock(int clock)
-{
- CGU.clk_pcr[clock] |= 1;
-}
-
-static void cgu_start_sel_stage_dividers(int bcr)
-{
- CGU.base_bcr[bcr] = 1;
-}
-
-/* Convert a pointer that points to IRAM (0x4xxxx) to a pointer that
- points to the uncached page (0x0xxxx) that is also mapped to IRAM. */
-static inline void *noncached(void *p)
-{
- return (void *)(((unsigned long)p) & 0xffff);
-}
-
-/* To avoid SRAM accesses while changing memory controller settings we
- run this routine from uncached copy of IRAM. All times are in CPU
- cycles. At CPU frequencies lower than 60 MHz we could use faster
- settings, but since DMA may access SRAM at any time, changing
- memory timings together with CPU frequency would be tricky. */
-static void do_set_mem_timings(void) ICODE_ATTR;
-static void do_set_mem_timings(void)
-{
- int old_irq = disable_irq_save();
- while ((EMC.status & 3) != 0);
- EMC.control = 5;
- EMCSTATIC0.waitrd = 6;
- EMCSTATIC0.waitwr = 5;
- EMCSTATIC1.waitrd = 5;
- EMCSTATIC1.waitwr = 4; /* OF uses 5 here */
- EMCSTATIC2.waitrd = 4;
- EMCSTATIC2.waitwr = 3;
- EMCSTATIC0.waitoen = 1;
- EMCSTATIC1.waitoen = 1;
- EMCSTATIC2.waitoen = 1;
- /* Enable write buffers for SRAM. */
-#ifndef DEBUG
- EMCSTATIC1.config = 0x80081;
-#endif
- EMC.control = 1;
- restore_irq(old_irq);
-}
-
-static void emc_set_mem_timings(void)
-{
- void (*f)(void) = noncached(do_set_mem_timings);
- (*f)();
-}
-
-static void cgu_set_sys_mult(int i)
-{
- cgu_set_sel_stage_input(PNX0101_SEL_STAGE_SYS, PNX0101_MAIN_CLOCK_FAST);
- cgu_set_sel_stage_input(PNX0101_SEL_STAGE_APB3, PNX0101_MAIN_CLOCK_FAST);
-
- PLL.lppdn = 1;
- PLL.lpfin = 1;
- PLL.lpmbyp = 0;
- PLL.lpdbyp = 0;
- PLL.lppsel = 1;
- PLL.lpmsel = i - 1;
- PLL.lppdn = 0;
- while (!PLL.lplock);
-
- cgu_configure_div(PNX0101_FIRST_DIV_SYS + 1, 1, (i == 5) ? 15 : 12);
- cgu_connect_div_to_clock(1, 0x11);
- cgu_enable_clock(0x11);
- cgu_start_sel_stage_dividers(PNX0101_BCR_SYS);
-
- cgu_set_sel_stage_input(PNX0101_SEL_STAGE_SYS,
- PNX0101_MAIN_CLOCK_MAIN_PLL);
- cgu_set_sel_stage_input(PNX0101_SEL_STAGE_APB3,
- PNX0101_MAIN_CLOCK_MAIN_PLL);
-}
-
-static void pnx0101_set_performance_mode(int mode)
-{
- int old = performance_mode;
- if (perf_modes[old].sys_mult != perf_modes[mode].sys_mult)
- cgu_set_sys_mult(perf_modes[mode].sys_mult);
- if (perf_modes[old].sys_div != perf_modes[mode].sys_div)
- cgu_configure_div(bus_divider, 1, perf_modes[mode].sys_div);
- performance_mode = mode;
-}
-
-static void pnx0101_init_clocks(void)
-{
- bus_divider = PNX0101_FIRST_DIV_SYS + (CGU.clk_esr[0] >> 1);
- performance_mode = 0;
- emc_set_mem_timings();
- pnx0101_set_performance_mode(2);
-
- cgu_set_sel_stage_input(PNX0101_SEL_STAGE_APB1,
- PNX0101_MAIN_CLOCK_FAST);
- cgu_reset_sel_stage_clocks(PNX0101_FIRST_ESR_APB1, PNX0101_N_ESR_APB1,
- PNX0101_FIRST_DIV_APB1, PNX0101_N_DIV_APB1);
- cgu_configure_div(PNX0101_FIRST_DIV_APB1, 1, 4);
- cgu_connect_div_to_clock(0, PNX0101_ESR_APB1);
- cgu_connect_div_to_clock(0, PNX0101_ESR_T0);
- cgu_connect_div_to_clock(0, PNX0101_ESR_T1);
- cgu_connect_div_to_clock(0, PNX0101_ESR_I2C);
- cgu_enable_clock(PNX0101_CLOCK_APB1);
- cgu_enable_clock(PNX0101_CLOCK_T0);
- cgu_enable_clock(PNX0101_CLOCK_T1);
- cgu_enable_clock(PNX0101_CLOCK_I2C);
-}
-
-#ifdef HAVE_ADJUSTABLE_CPU_FREQ
-void set_cpu_frequency(long frequency)
-{
- switch (frequency)
- {
- case CPUFREQ_MAX:
- pnx0101_set_performance_mode(2);
- cpu_frequency = CPUFREQ_MAX;
- break;
- case CPUFREQ_NORMAL:
- pnx0101_set_performance_mode(1);
- cpu_frequency = CPUFREQ_NORMAL;
- break;
- case CPUFREQ_DEFAULT:
- default:
- pnx0101_set_performance_mode(0);
- cpu_frequency = CPUFREQ_DEFAULT;
- break;
- }
-
-}
-#endif
-
-interrupt_handler_t interrupt_vector[0x1d] __attribute__ ((section(".idata")));
-
-#define IRQ_READ(reg, dest) \
- do { unsigned long v2; \
- do { \
- dest = (reg); \
- v2 = (reg); \
- } while ((dest != v2)); \
- } while (0);
-
-#define IRQ_WRITE_WAIT(reg, val, cond) \
- do { unsigned long v, v2; \
- do { \
- (reg) = (val); \
- v = (reg); \
- v2 = (reg); \
- } while ((v != v2) || !(cond)); \
- } while (0);
-
-static void undefined_int(void)
-{
-}
-
-void irq(void)
-{
- unsigned long n;
- IRQ_READ(INTVECTOR[0], n)
- (*(interrupt_vector[n >> 3]))();
-}
-
-void fiq(void)
-{
-}
-
-void irq_enable_int(int n)
-{
- IRQ_WRITE_WAIT(INTREQ[n], INTREQ_WEENABLE | INTREQ_ENABLE, v & 0x10000);
-}
-
-void irq_disable_int(int n)
-{
- IRQ_WRITE_WAIT(INTREQ[n], INTREQ_WEENABLE, (v & 0x10000) == 0);
-}
-
-void irq_set_int_handler(int n, interrupt_handler_t handler)
-{
- interrupt_vector[n] = handler;
-}
-
-void system_init(void)
-{
- int i;
-
- /* turn off watchdog */
- (*(volatile unsigned long *)0x80002804) = 0;
-
- /*
- IRQ_WRITE_WAIT(INTVECTOR[0], 0, v == 0);
- IRQ_WRITE_WAIT(INTVECTOR[1], 0, v == 0);
- IRQ_WRITE_WAIT(INTPRIOMASK[0], 0, v == 0);
- IRQ_WRITE_WAIT(INTPRIOMASK[1], 0, v == 0);
- */
-
- for (i = 1; i <= 0x1c; i++)
- {
- IRQ_WRITE_WAIT(INTREQ[i],
- INTREQ_WEPRIO | INTREQ_WETARGET |
- INTREQ_WEENABLE | INTREQ_WEACTVLO | 1,
- (v & 0x3010f) == 1);
- IRQ_WRITE_WAIT(INTREQ[i], INTREQ_WEENABLE, (v & 0x10000) == 0);
- IRQ_WRITE_WAIT(INTREQ[i], INTREQ_WEPRIO | 1, (v & 0xf) == 1);
- interrupt_vector[i] = undefined_int;
- }
- interrupt_vector[0] = undefined_int;
- pnx0101_init_clocks();
-}
-
-
-void system_reboot(void)
-{
- (*(volatile unsigned long *)0x80002804) = 1;
- while (1);
-}
-
-void system_exception_wait(void)
-{
- while (1);
-}
-
-int system_memory_guard(int newmode)
-{
- (void)newmode;
- return 0;
-}
diff --git a/firmware/target/arm/pnx0101/timer-pnx0101.c b/firmware/target/arm/pnx0101/timer-pnx0101.c
deleted file mode 100644
index 1ec1d2871f..0000000000
--- a/firmware/target/arm/pnx0101/timer-pnx0101.c
+++ /dev/null
@@ -1,81 +0,0 @@
-/***************************************************************************
-* __________ __ ___.
-* Open \______ \ ____ ____ | | _\_ |__ _______ ___
-* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
-* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
-* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
-* \/ \/ \/ \/ \/
-* $Id$
-*
-* Copyright (C) 2007 Tomasz Malesinski
-*
-* 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 "timer.h"
-
-static long cycles_new = 0;
-
-void TIMER1_ISR(void)
-{
- if (cycles_new > 0)
- {
- TIMER1.load = cycles_new - 1;
- cycles_new = 0;
- }
- if (pfn_timer != NULL)
- {
- cycles_new = -1;
- /* "lock" the variable, in case timer_set_period()
- * is called within pfn_timer() */
- pfn_timer();
- cycles_new = 0;
- }
- TIMER1.clr = 1; /* clear the interrupt */
-}
-
-bool timer_set(long cycles, bool start)
-{
- if (start)
- {
- if (pfn_unregister != NULL)
- {
- pfn_unregister();
- pfn_unregister = NULL;
- }
- TIMER1.ctrl &= ~0x80; /* disable the counter */
- TIMER1.ctrl |= 0x40; /* reload after counting down to zero */
- TIMER1.ctrl &= ~0xc; /* no prescaler */
- TIMER1.clr = 1; /* clear an interrupt event */
- }
- if (start || (cycles_new == -1)) /* within isr, cycles_new is "locked" */
- { /* enable timer */
- TIMER1.load = cycles - 1;
- TIMER1.ctrl |= 0x80; /* enable the counter */
- }
- else
- cycles_new = cycles;
-
- return true;
-}
-
-bool timer_start(void)
-{
- irq_set_int_handler(IRQ_TIMER1, TIMER1_ISR);
- irq_enable_int(IRQ_TIMER1);
- return true;
-}
-
-void timer_stop(void)
-{
- TIMER1.ctrl &= ~0x80; /* disable timer 1 */
- irq_disable_int(IRQ_TIMER1);
-}
diff --git a/firmware/target/arm/pp/mi4-loader.c b/firmware/target/arm/pp/mi4-loader.c
index 31b0065888..f609e3ff7a 100644
--- a/firmware/target/arm/pp/mi4-loader.c
+++ b/firmware/target/arm/pp/mi4-loader.c
@@ -27,100 +27,12 @@
#include "config.h"
#include "mi4-loader.h"
#include "loader_strerror.h"
-#include "crc32-mi4.h"
+#include "crc32.h"
#include "file.h"
#if defined(HAVE_BOOTDATA)
-#include "system.h"
-#include "bootdata.h"
-#include "crc32.h"
-
-/* Write bootdata into location in FIRMWARE marked by magic header
- * Assumes buffer is already loaded with the firmware image
- * We just need to find the location and write data into the
- * payload region along with the crc for later verification and use.
- * Returns payload len on success,
- * On error returns EKEY_NOT_FOUND
- */
-int write_bootdata(unsigned char* buf, int len, unsigned int boot_volume)
-{
- struct boot_data_t bl_boot_data;
- struct boot_data_t *fw_boot_data = NULL;
- int search_len = MIN(len, BOOT_DATA_SEARCH_SIZE) - sizeof(struct boot_data_t);
- int payload_len = EKEY_NOT_FOUND;
-
- /* search for boot data header prior to search_len */
- for(int i = 0;i < search_len;i++)
- {
- fw_boot_data = (struct boot_data_t*) &buf[i];
- if (fw_boot_data->magic[0] != BOOT_DATA_MAGIC0 ||
- fw_boot_data->magic[1] != BOOT_DATA_MAGIC1)
- continue;
-
- memset(&bl_boot_data.payload, 0, BOOT_DATA_PAYLOAD_SIZE);
- bl_boot_data.boot_volume = boot_volume;
-
- memset(fw_boot_data->payload, 0, fw_boot_data->length);
- /* determine maximum bytes we can write to firmware
- BOOT_DATA_PAYLOAD_SIZE is the size the bootloader expects */
- payload_len = MIN(BOOT_DATA_PAYLOAD_SIZE, fw_boot_data->length);
- fw_boot_data->length = payload_len;
- /* copy data to FIRMWARE bootdata struct */
- memcpy(fw_boot_data->payload, &bl_boot_data.payload, payload_len);
- /* crc will be used within the firmware to check validity of bootdata */
- fw_boot_data->crc = crc_32(fw_boot_data->payload, payload_len, 0xffffffff);
- break;
-
- }
- return payload_len;
-}
+#include "multiboot.h"
#endif /* HAVE_BOOTDATA */
-#ifdef HAVE_MULTIBOOT /* defined by config.h */
-/* Check in root of this <volume> for rockbox_main.<playername>
- * if this file empty or there is a single slash '/'
- * buf = '<volume#>/<rootdir>/<firmware(name)>\0'
- * If instead '/<*DIRECTORY*>' is supplied
- * addpath will be set to this DIRECTORY buf =
- * '/<volume#>/addpath/<rootdir>/<firmware(name)>\0'
- * On error returns Negative number or 0
- * On success returns bytes from snprintf
- * and generated path will be placed in buf
- * note: if supplied buffer is too small return will be
- * the number of bytes that would have been written
- */
-int get_redirect_dir(char* buf, int buffer_size, int volume,
- const char* rootdir, const char* firmware)
-{
- int fd;
- int f_offset;
- char add_path[MAX_PATH];
- /* Check in root of volume for rockbox_main.<playername> redirect */
- snprintf(add_path, sizeof(add_path), "/<%d>/"BOOT_REDIR, volume);
- fd = open(add_path, O_RDONLY);
- if (fd < 0)
- return EFILE_NOT_FOUND;
-
- /*clear add_path for re-use*/
- memset(add_path, 0, sizeof(add_path));
- f_offset = read(fd, add_path,sizeof(add_path));
- close(fd);
-
- for(int i = f_offset - 1;i > 0; i--)
- {
- /* strip control chars < SPACE or all if path doesn't start with '/' */
- if (add_path[i] < 0x20 || add_path[0] != '/')
- add_path[i] = '\0';
- }
- /* if '/add_path' is specified in rockbox_main.<playername>
- path is /<vol#>/add_path/rootdir/firmwarename
- if add_path is empty or '/' is missing from beginning
- path is /<vol#>/rootdir/firmwarename
- */
- return snprintf(buf, buffer_size, "/<%d>%s/%s/%s", volume, add_path,
- rootdir, firmware);
-}
-#endif /* HAVE_MULTIBOOT */
-
static inline unsigned int le2int(unsigned char* buf)
{
int32_t res = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
@@ -293,7 +205,7 @@ static int load_mi4_filename(unsigned char* buf,
return EREAD_IMAGE_FAILED;
/* Check CRC32 to see if we have a valid file */
- sum = chksum_crc32 (buf, mi4header.mi4size - MI4_HEADER_SIZE);
+ sum = crc_32r (buf, mi4header.mi4size - MI4_HEADER_SIZE, 0);
if(sum != mi4header.crc32)
return EBAD_CHKSUM;
diff --git a/firmware/target/arm/pp/pcm-pp.c b/firmware/target/arm/pp/pcm-pp.c
index 8434123a92..94b1c5ae10 100644
--- a/firmware/target/arm/pp/pcm-pp.c
+++ b/firmware/target/arm/pp/pcm-pp.c
@@ -327,6 +327,7 @@ void fiq_playback(void)
*/
asm volatile (
/* No external calls */
+ BEGIN_ARM_ASM_SYNTAX_UNIFIED
"sub lr, lr, #4 \n" /* Prepare return address */
"stmfd sp!, { lr } \n" /* stack lr so we can use it */
"ldr r12, =0xcf001040 \n" /* Some magic from iPodLinux ... */
@@ -349,8 +350,8 @@ void fiq_playback(void)
"bhi 0b \n" /* ... yes, continue */
"cmp r9, #0 \n" /* either FIFO full or size empty? */
- "stmneia r11, { r8-r9 } \n" /* save p and size, if not empty */
- "ldmnefd sp!, { pc }^ \n" /* RFE if not empty */
+ "stmiane r11, { r8-r9 } \n" /* save p and size, if not empty */
+ "ldmfdne sp!, { pc }^ \n" /* RFE if not empty */
/* Making external calls */
"1: \n"
@@ -363,7 +364,7 @@ void fiq_playback(void)
"mov lr, pc \n" /* long call (not in same section) */
"bx r3 \n"
"cmp r0, #0 \n" /* more data? */
- "ldmeqfd sp!, { r0-r3, pc }^ \n" /* no? -> exit */
+ "ldmfdeq sp!, { r0-r3, pc }^ \n" /* no? -> exit */
"ldr r14, [r10, #0x1c] \n" /* read IISFIFO_CFG to check FIFO status */
"ands r14, r14, #(0xe<<23) \n" /* r14 = (IIS_TX_FREE_COUNT & ~1) << 23 */
@@ -394,6 +395,7 @@ void fiq_playback(void)
"bne 3b \n" /* no? -> go return */
"b 2b \n" /* yes -> get even more */
".ltorg \n"
+ END_ARM_ASM_SYNTAX_UNIFIED
: /* These must only be integers! No regs */
: "i"(PCM_DMAST_OK), "i"(PCM_DMAST_STARTED));
}
@@ -512,19 +514,6 @@ void pcm_play_dma_postinit(void)
audiohw_postinit();
}
-const void * pcm_play_dma_get_peak_buffer(int *count)
-{
- unsigned long addr, size;
-
- int status = disable_fiq_save();
- addr = dma_play_data.addr;
- size = dma_play_data.size;
- restore_fiq(status);
-
- *count = size >> 2;
- return (void *)((addr + 2) & ~3);
-}
-
/****************************************************************************
** Recording DMA transfer
**/
diff --git a/firmware/target/arm/pp/system-pp502x.c b/firmware/target/arm/pp/system-pp502x.c
index d6dfad1b24..fb6fe28d91 100644
--- a/firmware/target/arm/pp/system-pp502x.c
+++ b/firmware/target/arm/pp/system-pp502x.c
@@ -559,8 +559,6 @@ void system_init(void)
/* to be done */
#elif defined (MROBE_100)
/* to be done */
-#elif defined (TATUNG_TPJ1022)
- /* to be done */
#elif defined(PBELL_VIBE500)
/* reset all allowed devices */
DEV_RS = 0x3ffffef8;
diff --git a/firmware/target/arm/pp/system-target.h b/firmware/target/arm/pp/system-target.h
index 1e947195bd..c26e9e1d06 100644
--- a/firmware/target/arm/pp/system-target.h
+++ b/firmware/target/arm/pp/system-target.h
@@ -175,13 +175,7 @@ extern unsigned char probed_ramsize;
int battery_default_capacity(void);
#endif
-
#ifdef BOOTLOADER
-#if defined(TATUNG_TPJ1022)
- /* Some targets don't like yielding in the bootloader - force
- * yield() to return without a context switch. */
-#define YIELD_KERNEL_HOOK() true
-#endif
#ifdef HAVE_BOOTLOADER_USB_MODE
void tick_stop(void);
diff --git a/firmware/target/arm/pp/usb-fw-pp502x.c b/firmware/target/arm/pp/usb-fw-pp502x.c
index b0fcfb83a4..a4d8e28a06 100644
--- a/firmware/target/arm/pp/usb-fw-pp502x.c
+++ b/firmware/target/arm/pp/usb-fw-pp502x.c
@@ -81,12 +81,6 @@
#define USB_GPIO_MASK 0x10
#define USB_GPIO_VAL 0x10
-#elif defined(TATUNG_TPJ1022)
- /* GPIO ? bit ? is usb detect (dummy value)*/
-#define USB_GPIO GPIOD
-#define USB_GPIO_MASK 0x10
-#define USB_GPIO_VAL 0x10
-
#elif defined(PBELL_VIBE500)
/* GPIO L bit 3 is usb detect */
#define USB_GPIO GPIOL
diff --git a/firmware/target/arm/rk27xx/lcdif-rk27xx.c b/firmware/target/arm/rk27xx/lcdif-rk27xx.c
index e6af0d978a..618b476480 100644
--- a/firmware/target/arm/rk27xx/lcdif-rk27xx.c
+++ b/firmware/target/arm/rk27xx/lcdif-rk27xx.c
@@ -198,9 +198,10 @@ static void create_llp(int x, int y, int width, int height)
width = width>>1;
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
/* build LLPs */
for (i=0; i<height; i++)
- llp_setup((void *)FBADDR(x,y+i),
+ llp_setup((void *)fbaddr(x,y+i),
(void*)(LCD_BUFF+((i%4)*4*width)),
&(scr_llp[i]),
width);
diff --git a/firmware/target/arm/rk27xx/pcm-rk27xx.c b/firmware/target/arm/rk27xx/pcm-rk27xx.c
index c0b4c45805..26e3ad87ba 100644
--- a/firmware/target/arm/rk27xx/pcm-rk27xx.c
+++ b/firmware/target/arm/rk27xx/pcm-rk27xx.c
@@ -263,18 +263,6 @@ void INT_HDMA(void)
}
}
-const void * pcm_play_dma_get_peak_buffer(int *count)
-{
- uint32_t addr;
-
- int old = disable_irq_save();
- addr = HDMA_CSRC0;
- *count = ((HDMA_CCNT0 & 0xffff)<<2);
- restore_interrupt(old);
-
- return (void*)addr;
-}
-
/****************************************************************************
** Recording DMA transfer
**/
diff --git a/firmware/target/arm/rk27xx/usb-drv-rk27xx.c b/firmware/target/arm/rk27xx/usb-drv-rk27xx.c
index 057ecf6ebc..77860b5494 100644
--- a/firmware/target/arm/rk27xx/usb-drv-rk27xx.c
+++ b/firmware/target/arm/rk27xx/usb-drv-rk27xx.c
@@ -117,7 +117,7 @@ static void setup_received(void)
setup_data[1] = SETUP2;
/* pass setup data to the upper layer */
- usb_core_control_request((struct usb_ctrlrequest*)setup_data);
+ usb_core_legacy_control_request((struct usb_ctrlrequest*)setup_data);
}
static int max_pkt_size(struct endpoint_t *endp)
@@ -355,7 +355,7 @@ int usb_drv_send_nonblocking(int endpoint, void *ptr, int length)
}
/* Setup a receive transfer. (non blocking) */
-int usb_drv_recv(int endpoint, void* ptr, int length)
+int usb_drv_recv_nonblocking(int endpoint, void* ptr, int length)
{
logf("udc: recv(%x)", endpoint);
struct endpoint_t *ep;
diff --git a/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c b/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c
index d784180e77..e75c3fe7c6 100644
--- a/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c
+++ b/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c
@@ -219,11 +219,3 @@ void fiq_handler(void)
pcm_play_dma_status_callback(PCM_DMAST_STARTED);
}
-
-const void * pcm_play_dma_get_peak_buffer(int *count)
-{
- unsigned long addr = DCSRC2;
- int cnt = DSTAT2;
- *count = (cnt & 0xFFFFF) >> 1;
- return (void *)((addr + 2) & ~3);
-}
diff --git a/firmware/target/arm/s3c2440/i2c-s3c2440.h b/firmware/target/arm/s3c2440/i2c-s3c2440.h
index 793ee213fd..8b739358af 100644
--- a/firmware/target/arm/s3c2440/i2c-s3c2440.h
+++ b/firmware/target/arm/s3c2440/i2c-s3c2440.h
@@ -41,6 +41,6 @@
/* IICLC */
#define I2C_FLT_ENB (1 << 2)
-void i2c_init(void);
+void i2c_init(void) INIT_ATTR;
void i2c_write(int addr, const unsigned char *data, int count);
diff --git a/firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c b/firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c
index 9574522586..999a53e3f9 100644
--- a/firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c
+++ b/firmware/target/arm/s3c2440/mini2440/pcm-mini2440.c
@@ -259,11 +259,3 @@ void fiq_handler(void)
pcm_play_dma_status_callback(PCM_DMAST_STARTED);
}
-
-const void * pcm_play_dma_get_peak_buffer(int *count)
-{
- unsigned long addr = DCSRC2;
- int cnt = DSTAT2;
- *count = (cnt & 0xFFFFF) >> 1;
- return (void *)((addr + 2) & ~3);
-}
diff --git a/firmware/target/arm/s5l8700/debug-s5l8700.c b/firmware/target/arm/s5l8700/debug-s5l8700.c
index ecb15df5d0..c42eac0438 100644
--- a/firmware/target/arm/s5l8700/debug-s5l8700.c
+++ b/firmware/target/arm/s5l8700/debug-s5l8700.c
@@ -112,7 +112,7 @@ bool dbg_hw_info(void)
_DEBUG_PRINTF("PMU:");
for(i=0;i<7;i++)
{
- char *device[] = {"(unknown)",
+ static const char * const device[] = {"(unknown)",
"(CLICKWHEEL)",
"(LCD)",
"(AUDIO)",
@@ -139,7 +139,7 @@ bool dbg_hw_info(void)
char line_cfg[4];
int abr_stat;
uint32_t abr_cnt;
- char *abrstatus[] = {"Idle", "Launched", "Counting", "Abnormal"};
+ static const char * const abrstatus[] = {"Idle", "Launched", "Counting", "Abnormal"};
uartc_port_get_line_info(&ser_port,
&tx_stat, &rx_stat, &tx_speed, &rx_speed, line_cfg);
diff --git a/firmware/target/arm/s5l8700/ipodnano2g/button-target.h b/firmware/target/arm/s5l8700/ipodnano2g/button-target.h
index 82f600d302..75bc191608 100644
--- a/firmware/target/arm/s5l8700/ipodnano2g/button-target.h
+++ b/firmware/target/arm/s5l8700/ipodnano2g/button-target.h
@@ -47,15 +47,20 @@ void ipod_4g_button_int(void);
/* Remote control's buttons */
#ifdef IPOD_ACCESSORY_PROTOCOL
+#define BUTTON_RC_DOWN 0x01000000
+#define BUTTON_RC_UP 0x00800000
+#define BUTTON_RC_SELECT 0x00400000
+#define BUTTON_RC_MENU 0x00200000
#define BUTTON_RC_PLAY 0x00100000
#define BUTTON_RC_STOP 0x00080000
-
#define BUTTON_RC_LEFT 0x00040000
#define BUTTON_RC_RIGHT 0x00020000
#define BUTTON_RC_VOL_UP 0x00010000
#define BUTTON_RC_VOL_DOWN 0x00008000
-#define BUTTON_REMOTE (BUTTON_RC_PLAY|BUTTON_RC_STOP\
+#define BUTTON_REMOTE (BUTTON_RC_UP|BUTTON_RC_DOWN \
+ |BUTTON_RC_SELECT|BUTTON_RC_PLAY \
+ |BUTTON_RC_PLAY|BUTTON_RC_STOP \
|BUTTON_RC_LEFT|BUTTON_RC_RIGHT\
|BUTTON_RC_VOL_UP|BUTTON_RC_VOL_DOWN)
#endif
diff --git a/firmware/target/arm/s5l8700/ipodnano2g/rtc-nano2g.c b/firmware/target/arm/s5l8700/ipodnano2g/rtc-nano2g.c
index 6f6b58ca35..a14bffb81c 100644
--- a/firmware/target/arm/s5l8700/ipodnano2g/rtc-nano2g.c
+++ b/firmware/target/arm/s5l8700/ipodnano2g/rtc-nano2g.c
@@ -45,9 +45,9 @@ int rtc_read_datetime(struct tm *tm)
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);
+ set_day_of_year(tm);
return 0;
}
diff --git a/firmware/target/arm/s5l8700/pcm-s5l8700.c b/firmware/target/arm/s5l8700/pcm-s5l8700.c
index 050e14451d..c7b6ed9a03 100644
--- a/firmware/target/arm/s5l8700/pcm-s5l8700.c
+++ b/firmware/target/arm/s5l8700/pcm-s5l8700.c
@@ -262,12 +262,6 @@ void pcm_dma_apply_settings(void)
pcm_dma_set_freq(pcm_fsel);
}
-const void * pcm_play_dma_get_peak_buffer(int *count)
-{
- *count = DMACTCNT0 >> 1;
- return (void *)(((DMACADDR0 + 2) & ~3) | 0x40000000);
-}
-
#ifdef HAVE_PCM_DMA_ADDRESS
void * pcm_dma_addr(void *addr)
{
diff --git a/firmware/target/arm/s5l8700/yps3/lcd-yps3.c b/firmware/target/arm/s5l8700/yps3/lcd-yps3.c
index a9830bca57..eec11e34b8 100644
--- a/firmware/target/arm/s5l8700/yps3/lcd-yps3.c
+++ b/firmware/target/arm/s5l8700/yps3/lcd-yps3.c
@@ -299,14 +299,15 @@ void lcd_update_rect(int x, int y, int width, int height)
{
fb_data* p;
int h, w;
-
+
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
if (lcd_type == 1) {
/* TODO implement and test */
lcd_set_window1(x, y, width, height);
lcd_set_position1(x, y);
-
+
for (h = 0; h < height; h++) {
- p = FBADDR(0,y);
+ p = fbaddr(0,y);
for (w = 0; w < LCD_WIDTH; w++) {
while (LCD_STATUS & 0x10);
LCD_WDATA = *p++;
@@ -317,9 +318,9 @@ void lcd_update_rect(int x, int y, int width, int height)
else {
lcd_set_window2(x, y, width, height);
lcd_set_position2(x, y);
-
+
for (h = 0; h < height; h++) {
- p = FBADDR(x,y);
+ p = fbaddr(x,y);
for (w = 0; w < width; w++) {
while (LCD_STATUS & 0x10);
LCD_WDATA = *p++;
diff --git a/firmware/target/arm/s5l8702/debug-s5l8702.c b/firmware/target/arm/s5l8702/debug-s5l8702.c
index 68b16f39f7..26b8e557a0 100644
--- a/firmware/target/arm/s5l8702/debug-s5l8702.c
+++ b/firmware/target/arm/s5l8702/debug-s5l8702.c
@@ -104,7 +104,7 @@ bool dbg_hw_info(void)
_DEBUG_PRINTF("PMU:");
for(i=0;i<7;i++)
{
- char *device[] = {"unknown",
+ static const char *const device[] = {"unknown",
"unknown",
"LCD",
"AUDIO",
@@ -157,7 +157,7 @@ bool dbg_hw_info(void)
char line_cfg[4];
int abr_stat;
uint32_t abr_cnt;
- char *abrstatus[] = {"Idle", "Launched", "Counting", "Abnormal"};
+ static const char * const abrstatus[] = {"Idle", "Launched", "Counting", "Abnormal"};
uartc_port_get_line_info(&ser_port,
&tx_stat, &rx_stat, &tx_speed, &rx_speed, line_cfg);
diff --git a/firmware/target/arm/s5l8702/ipod6g/button-target.h b/firmware/target/arm/s5l8702/ipod6g/button-target.h
index ed17fc4baa..a5c4771b7c 100644
--- a/firmware/target/arm/s5l8702/ipod6g/button-target.h
+++ b/firmware/target/arm/s5l8702/ipod6g/button-target.h
@@ -47,15 +47,20 @@ void ipod_4g_button_int(void);
/* Remote control's buttons */
#ifdef IPOD_ACCESSORY_PROTOCOL
+#define BUTTON_RC_DOWN 0x01000000
+#define BUTTON_RC_UP 0x00800000
+#define BUTTON_RC_SELECT 0x00400000
+#define BUTTON_RC_MENU 0x00200000
#define BUTTON_RC_PLAY 0x00100000
#define BUTTON_RC_STOP 0x00080000
-
#define BUTTON_RC_LEFT 0x00040000
#define BUTTON_RC_RIGHT 0x00020000
#define BUTTON_RC_VOL_UP 0x00010000
#define BUTTON_RC_VOL_DOWN 0x00008000
-#define BUTTON_REMOTE (BUTTON_RC_PLAY|BUTTON_RC_STOP\
+#define BUTTON_REMOTE (BUTTON_RC_UP|BUTTON_RC_DOWN \
+ |BUTTON_RC_SELECT|BUTTON_RC_PLAY \
+ |BUTTON_RC_PLAY|BUTTON_RC_STOP \
|BUTTON_RC_LEFT|BUTTON_RC_RIGHT\
|BUTTON_RC_VOL_UP|BUTTON_RC_VOL_DOWN)
#endif
diff --git a/firmware/target/arm/s5l8702/ipod6g/rtc-6g.c b/firmware/target/arm/s5l8702/ipod6g/rtc-6g.c
index 384cded758..bf1b18035a 100644
--- a/firmware/target/arm/s5l8702/ipod6g/rtc-6g.c
+++ b/firmware/target/arm/s5l8702/ipod6g/rtc-6g.c
@@ -45,9 +45,9 @@ int rtc_read_datetime(struct tm *tm)
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);
+ set_day_of_year(tm);
return 0;
}
diff --git a/firmware/target/arm/s5l8702/ipod6g/storage_ata-6g.c b/firmware/target/arm/s5l8702/ipod6g/storage_ata-6g.c
index 36d119aff3..72c7cc78c7 100644
--- a/firmware/target/arm/s5l8702/ipod6g/storage_ata-6g.c
+++ b/firmware/target/arm/s5l8702/ipod6g/storage_ata-6g.c
@@ -30,11 +30,26 @@
#include "s5l8702.h"
#include "led.h"
-
#ifndef ATA_RETRIES
#define ATA_RETRIES 3
#endif
+#define CMD_READ_SECTORS 0x20
+#define CMD_READ_DMA_EXT 0x25
+#define CMD_READ_MULTIPLE_EXT 0x29
+#define CMD_WRITE_SECTORS 0x30
+#define CMD_WRITE_DMA_EXT 0x35
+#define CMD_WRITE_MULTIPLE_EXT 0x39
+#ifdef HAVE_ATA_SMART
+#define CMD_SMART 0xB0
+#endif
+#define CMD_READ_MULTIPLE 0xC4
+#define CMD_WRITE_MULTIPLE 0xC5
+#define CMD_READ_DMA 0xC8
+#define CMD_WRITE_DMA 0xCA
+#define CMD_STANDBY_IMMEDIATE 0xE0
+#define CMD_IDENTIFY 0xEC
+#define CMD_SET_FEATURES 0xEF
#define CEATA_POWERUP_TIMEOUT 20000000
#define CEATA_COMMAND_TIMEOUT 1000000
@@ -482,7 +497,7 @@ static int ata_identify(uint16_t* buf)
if (ceata)
{
memset(ceata_taskfile, 0, 16);
- ceata_taskfile[0xf] = 0xec;
+ ceata_taskfile[0xf] = CMD_IDENTIFY;
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);
@@ -491,7 +506,7 @@ static int ata_identify(uint16_t* buf)
{
PASS_RC(ata_wait_for_not_bsy(10000000), 1, 0);
ata_write_cbr(&ATA_PIO_DVR, 0);
- ata_write_cbr(&ATA_PIO_CSD, 0xec);
+ ata_write_cbr(&ATA_PIO_CSD, CMD_IDENTIFY);
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);
}
@@ -515,7 +530,7 @@ static int ata_set_feature(uint32_t feature, uint32_t param)
memset(ceata_taskfile, 0, 16);
ceata_taskfile[0x1] = feature;
ceata_taskfile[0x2] = param;
- ceata_taskfile[0xf] = 0xef;
+ ceata_taskfile[0xf] = CMD_SET_FEATURES;
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);
@@ -526,7 +541,7 @@ static int ata_set_feature(uint32_t feature, uint32_t param)
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);
+ ata_write_cbr(&ATA_PIO_CSD, CMD_SET_FEATURES);
PASS_RC(ata_wait_for_rdy(2000000), 2, 1);
}
return 0;
@@ -690,7 +705,7 @@ static void ata_power_down(void)
if (ceata)
{
memset(ceata_taskfile, 0, 16);
- ceata_taskfile[0xf] = 0xe0;
+ ceata_taskfile[0xf] = CMD_STANDBY_IMMEDIATE;
ceata_wait_idle();
ceata_write_multiple_register(0, ceata_taskfile, 16);
ceata_wait_idle();
@@ -701,7 +716,7 @@ static void ata_power_down(void)
{
ata_wait_for_rdy(1000000);
ata_write_cbr(&ATA_PIO_DVR, 0);
- ata_write_cbr(&ATA_PIO_CSD, 0xe0);
+ ata_write_cbr(&ATA_PIO_CSD, CMD_STANDBY_IMMEDIATE);
ata_wait_for_rdy(1000000);
sleep(HZ / 30);
ATA_CONTROL = 0;
@@ -730,7 +745,7 @@ static int ata_rw_chunk_internal(uint64_t sector, uint32_t cnt, void* buffer, bo
ceata_taskfile[0xb] = sector << 3;
ceata_taskfile[0xc] = sector >> 5;
ceata_taskfile[0xd] = sector >> 13;
- ceata_taskfile[0xf] = write ? 0x35 : 0x25;
+ ceata_taskfile[0xf] = write ? CMD_WRITE_DMA_EXT : CMD_READ_DMA_EXT;
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);
@@ -750,8 +765,10 @@ static int ata_rw_chunk_internal(uint64_t sector, uint32_t cnt, void* buffer, bo
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);
+ if (write)
+ ata_write_cbr(&ATA_PIO_CSD, ata_dma ? CMD_WRITE_DMA_EXT : CMD_WRITE_MULTIPLE_EXT);
+ else
+ ata_write_cbr(&ATA_PIO_CSD, ata_dma ? CMD_READ_DMA_EXT : CMD_READ_MULTIPLE_EXT);
}
else
{
@@ -760,8 +777,10 @@ static int ata_rw_chunk_internal(uint64_t sector, uint32_t cnt, void* buffer, bo
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 (write)
+ ata_write_cbr(&ATA_PIO_CSD, ata_dma ? CMD_WRITE_DMA : CMD_WRITE_SECTORS);
+ else
+ ata_write_cbr(&ATA_PIO_CSD, ata_dma ? CMD_READ_DMA : CMD_READ_MULTIPLE);
}
if (ata_dma)
{
@@ -1017,7 +1036,7 @@ static int ata_smart(uint16_t* buf)
ceata_taskfile[0xc] = 0x4f;
ceata_taskfile[0xd] = 0xc2;
ceata_taskfile[0xe] = BIT(6);
- ceata_taskfile[0xf] = 0xb0;
+ ceata_taskfile[0xf] = CMD_SMART;
PASS_RC(ceata_wait_idle(), 3, 1);
if (((uint8_t*)ata_identify_data)[54] != 'A') /* Model != aAmsung */
{
@@ -1037,7 +1056,7 @@ static int ata_smart(uint16_t* buf)
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);
+ ata_write_cbr(&ATA_PIO_CSD, CMD_SMART);
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);
}
diff --git a/firmware/target/arm/s5l8702/pcm-s5l8702.c b/firmware/target/arm/s5l8702/pcm-s5l8702.c
index 33fe637770..d4570f3a7f 100644
--- a/firmware/target/arm/s5l8702/pcm-s5l8702.c
+++ b/firmware/target/arm/s5l8702/pcm-s5l8702.c
@@ -216,13 +216,6 @@ void pcm_play_dma_postinit(void)
audiohw_postinit();
}
-const void* pcm_play_dma_get_peak_buffer(int *count)
-{
- void *addr = dmac_ch_get_info(&dma_play_ch, count, NULL);
- *count >>= 2; /* bytes to samples */
- return addr; /* aligned to dest burst */
-}
-
#ifdef HAVE_PCM_DMA_ADDRESS
void * pcm_dma_addr(void *addr)
{
diff --git a/firmware/target/arm/samsung/yh920/lcd-yh920.c b/firmware/target/arm/samsung/yh920/lcd-yh920.c
index 06aa3d718d..6a579f382a 100644
--- a/firmware/target/arm/samsung/yh920/lcd-yh920.c
+++ b/firmware/target/arm/samsung/yh920/lcd-yh920.c
@@ -253,13 +253,14 @@ void lcd_update_rect(int x, int y, int width, int height)
if(ymax >= LCD_FBHEIGHT)
ymax = LCD_FBHEIGHT-1;
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
/* Copy specified rectange bitmap to hardware */
for (; y <= ymax; y++)
{
lcd_write_reg(LCD_CNTL_PAGE, y);
lcd_write_reg(LCD_CNTL_COLUMN, x);
- addr = FBADDR(x,y);
+ addr = fbaddr(x,y);
lcd_send_cmd(LCD_CNTL_DATA_WRITE);
lcd_write_data(addr, width);
diff --git a/firmware/target/arm/system-arm.h b/firmware/target/arm/system-arm.h
index e5d3f35001..af16346b51 100644
--- a/firmware/target/arm/system-arm.h
+++ b/firmware/target/arm/system-arm.h
@@ -382,7 +382,7 @@ static inline uint32_t swaw32_hw(uint32_t value)
}
-#if defined(CPU_TCC780X) || defined(CPU_TCC77X) /* Single core only for now */ \
+#if defined(CPU_TCC780X) /* Single core only for now */ \
|| CONFIG_CPU == IMX31L || CONFIG_CPU == DM320 || CONFIG_CPU == AS3525 \
|| CONFIG_CPU == S3C2440 || CONFIG_CPU == S5L8701 || CONFIG_CPU == AS3525v2 \
|| CONFIG_CPU == S5L8702
diff --git a/firmware/target/arm/tatung/app.lds b/firmware/target/arm/tatung/app.lds
deleted file mode 100644
index 856cc52bb0..0000000000
--- a/firmware/target/arm/tatung/app.lds
+++ /dev/null
@@ -1,2 +0,0 @@
-#include "config.h"
-#include "../pp/app-pp.lds"
diff --git a/firmware/target/arm/tatung/boot.lds b/firmware/target/arm/tatung/boot.lds
deleted file mode 100644
index 33b826bec8..0000000000
--- a/firmware/target/arm/tatung/boot.lds
+++ /dev/null
@@ -1,2 +0,0 @@
-#include "config.h"
-#include "../pp/boot-pp.lds"
diff --git a/firmware/target/arm/tatung/tpj1022/button-target.h b/firmware/target/arm/tatung/tpj1022/button-target.h
deleted file mode 100644
index 1143c1b1da..0000000000
--- a/firmware/target/arm/tatung/tpj1022/button-target.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2006 by Robert Kukla
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ****************************************************************************/
-
-#ifndef _BUTTON_TARGET_H_
-#define _BUTTON_TARGET_H_
-
-#define HAS_BUTTON_HOLD
-
-/* Main unit's buttons */
-
-#define BUTTON_VOL_DOWN 0x00000001
-
-/* bit position in GPIOA */
-#define BUTTON_REW 0x00000002
-#define BUTTON_FF 0x00000004
-#define BUTTON_POWER 0x00000008
-#define BUTTON_UP 0x00000010
-#define BUTTON_DOWN 0x00000020
-#define BUTTON_AB 0x00000040
-#define BUTTON_RIGHT 0x00000080
-
-/* still unknown */
-#define BUTTON_MENU 0x00000100
-#define BUTTON_REC 0x00000200
-#define BUTTON_VOL_UP 0x00000400
-#define BUTTON_LEFT 0x00000800
-
-#define BUTTON_MAIN 0x00000FFF
-
-#define POWEROFF_BUTTON BUTTON_POWER
-#define POWEROFF_COUNT 10
-
-#endif /* _BUTTON_TARGET_H_ */
diff --git a/firmware/target/arm/tatung/tpj1022/button-tpj1022.c b/firmware/target/arm/tatung/tpj1022/button-tpj1022.c
deleted file mode 100644
index ce9d7ab24d..0000000000
--- a/firmware/target/arm/tatung/tpj1022/button-tpj1022.c
+++ /dev/null
@@ -1,49 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2006 by Robert Kukla
- *
- * 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 "button.h"
-
-bool button_hold(void)
-{
- return (GPIOK_INPUT_VAL & 0x40) ? true : false;
-}
-
-int button_read_device(void)
-{
- int btn = BUTTON_NONE;
-
- if (!button_hold())
- {
- btn = (GPIOA_INPUT_VAL & 0xfe) ^ 0xfe;
-
- if ((GPIOK_INPUT_VAL & 0x20) == 0) btn |= BUTTON_VOL_DOWN;
-
- /* to be found
- if ((GPIO?_INPUT_VAL & 0x??) == 0) btn |= BUTTON_MENU;
- if ((GPIO?_INPUT_VAL & 0x??) == 0) btn |= BUTTON_REC;
- if ((GPIO?_INPUT_VAL & 0x??) == 0) btn |= BUTTON_VOL_UP;
- if ((GPIO?_INPUT_VAL & 0x??) == 0) btn |= BUTTON_LEFT;
- */
- }
-
- return btn;
-}
diff --git a/firmware/target/arm/tatung/tpj1022/lcd-tpj1022.c b/firmware/target/arm/tatung/tpj1022/lcd-tpj1022.c
deleted file mode 100644
index bf29ba092e..0000000000
--- a/firmware/target/arm/tatung/tpj1022/lcd-tpj1022.c
+++ /dev/null
@@ -1,85 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2006 by Barry Wardell
- *
- * 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 "cpu.h"
-#include "lcd.h"
-#include "kernel.h"
-#include "system.h"
-
-/*** hardware configuration ***/
-
-void lcd_set_contrast(int val)
-{
- /* TODO: Implement lcd_set_contrast() */
- (void)val;
-}
-
-void lcd_set_invert_display(bool yesno)
-{
- /* TODO: Implement lcd_set_invert_display() */
- (void)yesno;
-}
-
-/* turn the display upside down (call lcd_update() afterwards) */
-void lcd_set_flip(bool yesno)
-{
- /* TODO: Implement lcd_set_flip() */
- (void)yesno;
-}
-
-/* LCD init */
-void lcd_init_device(void)
-{
-
-}
-
-/*** update functions ***/
-
-/* Performance function to 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)
-{
- (void)src;
- (void)src_x;
- (void)src_y;
- (void)stride;
- (void)x;
- (void)y;
- (void)width;
- (void)height;
-}
-
-/* Update a fraction of the display. */
-void lcd_update_rect(int x0, int y0, int width, int height)
-{
- (void)x0;
- (void)y0;
- (void)width;
- (void)height;
-}
-
-/* Update the display.
- This must be called after all other LCD functions that change the display. */
-void lcd_update(void)
-{
- lcd_update_rect(0, 0, LCD_WIDTH, LCD_HEIGHT);
-}
diff --git a/firmware/target/arm/tatung/tpj1022/power-tpj1022.c b/firmware/target/arm/tatung/tpj1022/power-tpj1022.c
deleted file mode 100644
index fa46f34daa..0000000000
--- a/firmware/target/arm/tatung/tpj1022/power-tpj1022.c
+++ /dev/null
@@ -1,63 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2006 by Barry Wardell
- *
- * 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.
- *
- ****************************************************************************/
-
-/* Created from power.c using some iPod code, and some custom stuff based on
- GPIO analysis
-*/
-
-#include "config.h"
-#include "cpu.h"
-#include <stdbool.h>
-#include "kernel.h"
-#include "system.h"
-#include "power.h"
-#include "logf.h"
-#include "usb.h"
-
-void power_init(void)
-{
-}
-
-unsigned int power_input_status(void)
-{
- return POWER_INPUT_NONE;
-}
-
-void ide_power_enable(bool on)
-{
- (void)on;
- /* We do nothing on the iPod */
-}
-
-
-bool ide_powered(void)
-{
- /* pretend we are always powered - we don't turn it off on the ipod */
- return true;
-}
-
-void power_off(void)
-{
- /* Give things a second to settle before cutting power */
- sleep(HZ);
-
- //GPIOF_OUTPUT_VAL &=~ 0x20;
-}
diff --git a/firmware/target/arm/tatung/tpj1022/powermgmt-tpj1022.c b/firmware/target/arm/tatung/tpj1022/powermgmt-tpj1022.c
deleted file mode 100644
index cc91012fcd..0000000000
--- a/firmware/target/arm/tatung/tpj1022/powermgmt-tpj1022.c
+++ /dev/null
@@ -1,62 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2002 by Heikki Hannikainen, Uwe Freese
- * Revisions copyright (C) 2005 by Gerald Van Baren
- *
- * 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 "adc.h"
-#include "powermgmt.h"
-
-/* FIXME: All voltages copied from H10 according to the battery type given
- * in config-tpj1022.h at the time of splitting. This probably needs changing
- * if that port ever gets up to speed. */
-
-const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] =
-{
- 3760
-};
-
-const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] =
-{
- 3650
-};
-
-/* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */
-const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11] =
-{
- { 3760, 3800, 3850, 3870, 3900, 3950, 4020, 4070, 4110, 4180, 4240 }
-};
-
-#if CONFIG_CHARGING
-/* voltages (millivolt) of 0%, 10%, ... 100% when charging enabled */
-const unsigned short percent_to_volt_charge[11] =
-{
- 3990, 4030, 4060, 4080, 4100, 4120, 4150, 4180, 4220, 4260, 4310
-};
-#endif /* CONFIG_CHARGING */
-
-#define BATTERY_SCALE_FACTOR 6000
-/* full-scale ADC readout (2^10) in millivolt */
-
-/* Returns battery voltage from ADC [millivolts] */
-int _battery_voltage(void)
-{
- return (adc_read(ADC_UNREG_POWER) * BATTERY_SCALE_FACTOR) >> 10;
-}
diff --git a/firmware/target/arm/tcc77x/adc-tcc77x.c b/firmware/target/arm/tcc77x/adc-tcc77x.c
deleted file mode 100644
index f48528639e..0000000000
--- a/firmware/target/arm/tcc77x/adc-tcc77x.c
+++ /dev/null
@@ -1,121 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2007 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 "cpu.h"
-#include "system.h"
-#include "kernel.h"
-#include "thread.h"
-#include "string.h"
-#include "adc.h"
-
-/**************************************************************************
- ** The A/D conversion is done every tick, in three steps:
- **
- ** 1) On the tick interrupt, the conversion of channels 0-3 is started, and
- ** the A/D interrupt is enabled.
- **
- ** 2) After the conversion is done, an interrupt
- ** is generated at level 1, which is the same level as the tick interrupt
- ** itself. This interrupt will be pending until the tick interrupt is
- ** finished.
- ** When the A/D interrupt is finally served, it will read the results
- ** from the first conversion and start the conversion of channels 4-7.
- **
- ** 3) When the conversion of channels 4-7 is finished, the interrupt is
- ** triggered again, and the results are read. This time, no new
- ** conversion is started, it will be done in the next tick interrupt.
- **
- ** Thus, each channel will be updated HZ times per second.
- **
- *************************************************************************/
-
-static int channel_group;
-static unsigned short adcdata[8];
-
-/* Tick task */
-static void adc_tick(void)
-{
- /* Start a conversion of channels 0-3. This will trigger an interrupt,
- and the interrupt handler will take care of channels 4-7. */
-
- int i;
-
- PCLKCFG6 |= (1<<15); /* Enable ADC clock */
-
- channel_group = 0;
-
- /* Start converting the first 4 channels */
- for (i = 0; i < 4; i++)
- ADCCON = i;
-
-}
-
-/* IRQ handler */
-void ADC(void)
-{
- int num;
- int i;
- uint32_t adc_status;
-
- do
- {
- adc_status = ADCSTATUS;
- num = (adc_status>>24) & 7;
- if (num) adcdata[(adc_status >> 16) & 0x7] = adc_status & 0x3ff;
- } while (num);
-
-
- if (channel_group == 0)
- {
- /* Start conversion of channels 4-7 */
- for (i = 4; i < 8; i++)
- ADCCON = i;
-
- channel_group = 1;
- }
- else
- {
- PCLKCFG6 &= ~(1<<15); /* Disable ADC clock */
- }
-}
-
-unsigned short adc_read(int channel)
-{
- return adcdata[channel];
-}
-
-void adc_init(void)
-{
- /* Initialize ADC clocks */
- PCLKCFG6 = (PCLKCFG6 & 0xffff0000) | 4004;
-
- ADCCON = (1<<4); /* Leave standby mode */
-
- /* IRQ enable, auto power-down, single-mode */
- ADCCFG |= (1<<3) | (1<<1) | (1<<0);
-
- /* Unmask ADC IRQ */
- IEN |= ADC_IRQ_MASK;
-
- tick_add_task(adc_tick);
-
- sleep(2); /* Ensure adc_data[] contains data before returning */
-}
diff --git a/firmware/target/arm/tcc77x/app.lds b/firmware/target/arm/tcc77x/app.lds
deleted file mode 100644
index 991b7156fe..0000000000
--- a/firmware/target/arm/tcc77x/app.lds
+++ /dev/null
@@ -1,99 +0,0 @@
-#include "config.h"
-
-ENTRY(start)
-OUTPUT_FORMAT(elf32-littlearm)
-OUTPUT_ARCH(arm)
-STARTUP(target/arm/tcc77x/crt0.o)
-
-#define PLUGINSIZE PLUGIN_BUFFER_SIZE
-#define CODECSIZE CODEC_SIZE
-
-#define DRAMSIZE (MEMORYSIZE * 0x100000) - PLUGINSIZE - CODECSIZE
-
-#define DRAMORIG 0x20000000
-#define IRAMORIG 0x00000000
-#define IRAMSIZE 64K
-
-
-/* End of the audio buffer, where the codec buffer starts */
-#define ENDAUDIOADDR (DRAMORIG + DRAMSIZE)
-
-/* Where the codec buffer ends, and the plugin buffer starts */
-#define ENDADDR (ENDAUDIOADDR + CODECSIZE)
-
-
-MEMORY
-{
-#ifdef TCCBOOT
- DRAM : ORIGIN = DRAMORIG + DRAMSIZE - 0x100000, LENGTH = 0x100000
-#else
- DRAM : ORIGIN = DRAMORIG, LENGTH = DRAMSIZE
-#endif
- IRAM : ORIGIN = IRAMORIG, LENGTH = IRAMSIZE
-}
-
-SECTIONS
-{
- .text : {
- loadaddress = .;
- _loadaddress = .;
- . = ALIGN(0x200);
- *(.init.text)
- *(.text)
- *(.text*)
- *(.glue_7)
- *(.glue_7t)
- } > DRAM
-
- .data : {
- *(.icode)
- *(.irodata)
- *(.idata)
- *(.data*)
- *(.rodata.*)
- *(.rodata)
- . = ALIGN(0x4);
- _dataend = . ;
- } > DRAM
-
- .stack :
- {
- *(.stack)
- _stackbegin = .;
- stackbegin = .;
- . += 0x2000;
- _stackend = .;
- stackend = .;
- } > DRAM
-
- .bss : {
- _edata = .;
- *(.bss*);
- *(.ibss);
- *(COMMON)
- _end = .;
- } > DRAM
- .audiobuf ALIGN(4) :
- {
- _audiobuffer = .;
- audiobuffer = .;
- } > DRAM
-
- .audiobufend ENDAUDIOADDR:
- {
- audiobufend = .;
- _audiobufend = .;
- } > DRAM
-
- .codec ENDAUDIOADDR:
- {
- codecbuf = .;
- _codecbuf = .;
- }
-
- .plugin ENDADDR:
- {
- _pluginbuf = .;
- pluginbuf = .;
- }
-}
diff --git a/firmware/target/arm/tcc77x/ata-nand-target.h b/firmware/target/arm/tcc77x/ata-nand-target.h
deleted file mode 100644
index 93139a16d5..0000000000
--- a/firmware/target/arm/tcc77x/ata-nand-target.h
+++ /dev/null
@@ -1,33 +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.
- *
- ****************************************************************************/
-#ifndef ATA_TARGET_H
-#define ATA_TARGET_H
-
-/* GPIOs */
-
-#define NAND_GPIO_SET(n) GPIOC |= n
-#define NAND_GPIO_CLEAR(n) GPIOC &= (~n)
-#define NAND_GPIO_OUT_EN(n) GPIOC_DIR |= n
-
-#define CS_GPIO_BIT (1<<24) /* Chip Select */
-#define WE_GPIO_BIT (1<<25) /* Write Enable */
-
-#endif
diff --git a/firmware/target/arm/tcc77x/boot.lds b/firmware/target/arm/tcc77x/boot.lds
deleted file mode 100644
index 6303de0c34..0000000000
--- a/firmware/target/arm/tcc77x/boot.lds
+++ /dev/null
@@ -1,63 +0,0 @@
-#include "config.h"
-
-ENTRY(start)
-OUTPUT_FORMAT(elf32-littlearm)
-OUTPUT_ARCH(arm)
-STARTUP(target/arm/tcc77x/crt0.o)
-
-#define DRAMSIZE (MEMORYSIZE * 0x100000)
-
-#define DRAMORIG 0x20000000
-#define IRAMORIG 0x00000000
-#define IRAMSIZE 64K
-
-
-MEMORY
-{
-#ifdef TCCBOOT
- DRAM : ORIGIN = DRAMORIG + DRAMSIZE - 0x100000, LENGTH = 0x100000
-#else
- DRAM : ORIGIN = DRAMORIG, LENGTH = DRAMSIZE
-#endif
- IRAM : ORIGIN = IRAMORIG, LENGTH = IRAMSIZE
-}
-
-SECTIONS
-{
- .text : {
- *(.init.text)
- *(.text)
- *(.text*)
- *(.glue_7)
- *(.glue_7t)
- } > DRAM
-
- .data : {
- *(.icode)
- *(.irodata)
- *(.idata)
- *(.data*)
- *(.rodata.*)
- *(.rodata)
- . = ALIGN(0x4);
- _dataend = . ;
- } > DRAM
-
- .stack (NOLOAD) :
- {
- *(.stack)
- _stackbegin = .;
- stackbegin = .;
- . += 0x2000;
- _stackend = .;
- stackend = .;
- } > DRAM
-
- .bss (NOLOAD) : {
- _edata = .;
- *(.bss*);
- *(.ibss);
- *(COMMON)
- _end = .;
- } > DRAM
-}
diff --git a/firmware/target/arm/tcc77x/c100/button-c100.c b/firmware/target/arm/tcc77x/c100/button-c100.c
deleted file mode 100644
index 47b004eb61..0000000000
--- a/firmware/target/arm/tcc77x/c100/button-c100.c
+++ /dev/null
@@ -1,64 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2007 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 "cpu.h"
-#include "button.h"
-
-void button_init_device(void)
-{
- GPIOA_DIR |= 0xC;
-}
-
-int button_read_device(void)
-{
- int btn = BUTTON_NONE;
-
- if (!button_hold())
- {
- GPIOA |= 0x4;
- GPIOA &= ~0x8;
-
- int i=20; while (i--);
-
- if (GPIOA & 0x10) btn |= BUTTON_UP;
- if (GPIOA & 0x20) btn |= BUTTON_RIGHT;
- if (GPIOA & 0x40) btn |= BUTTON_LEFT;
-
- GPIOA |= 0x8;
- GPIOA &= ~0x4;
-
- i=20; while (i--);
-
- if (GPIOA & 0x10) btn |= BUTTON_VOLUP;
- if (GPIOA & 0x20) btn |= BUTTON_VOLDOWN;
- if (GPIOA & 0x40) btn |= BUTTON_DOWN;
-
- if (GPIOA & 0x80) btn |= BUTTON_SELECT;
- if (GPIOA & 0x100) btn |= BUTTON_POWER;
- }
- return btn;
-}
-
-bool button_hold(void)
-{
- return (GPIOA & 0x2);
-}
diff --git a/firmware/target/arm/tcc77x/c100/lcd-S6B33B2.c b/firmware/target/arm/tcc77x/c100/lcd-S6B33B2.c
deleted file mode 100644
index c53aadaf30..0000000000
--- a/firmware/target/arm/tcc77x/c100/lcd-S6B33B2.c
+++ /dev/null
@@ -1,286 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2007 by Mark Arigo
- *
- * 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 "cpu.h"
-#include "lcd.h"
-#include "kernel.h"
-#include "system.h"
-
-/* Display status */
-static unsigned lcd_yuv_options SHAREDBSS_ATTR = 0;
-
-/* LCD command set for Samsung S6B33B2 */
-#define R_NOP 0x00
-#define R_OSCILLATION_MODE 0x02
-#define R_DRIVER_OUTPUT_MODE 0x10
-#define R_DCDC_SET 0x20
-#define R_BIAS_SET 0x22
-#define R_DCDC_CLOCK_DIV 0x24
-#define R_DCDC_AMP_ONOFF 0x26
-#define R_TEMP_COMPENSATION 0x28
-#define R_CONTRAST_CONTROL1 0x2a
-#define R_CONTRAST_CONTROL2 0x2b
-#define R_STANDBY_OFF 0x2c
-#define R_STANDBY_ON 0x2d
-#define R_DDRAM_BURST_OFF 0x2e
-#define R_DDRAM_BURST_ON 0x2f
-#define R_ADDRESSING_MODE 0x30
-#define R_ROW_VECTOR_MODE 0x32
-#define R_N_LINE_INVERSION 0x34
-#define R_FRAME_FREQ_CONTROL 0x36
-#define R_RED_PALETTE 0x38
-#define R_GREEN_PALETTE 0x3a
-#define R_BLUE_PALETTE 0x3c
-#define R_ENTRY_MODE 0x40
-#define R_X_ADDR_AREA 0x42
-#define R_Y_ADDR_AREA 0x43
-#define R_RAM_SKIP_AREA 0x45
-#define R_DISPLAY_OFF 0x50
-#define R_DISPLAY_ON 0x51
-#define R_SPEC_DISPLAY_PATTERN 0x53
-#define R_PARTIAL_DISPLAY_MODE 0x55
-#define R_PARTIAL_START_LINE 0x56
-#define R_PARTIAL_END_LINE 0x57
-#define R_AREA_SCROLL_MODE 0x59
-#define R_SCROLL_START_LINE 0x5a
-#define R_DATA_FORMAT_SELECT 0x60
-
-/* TCC77x specific defines */
-#define LCD_BASE 0x50000000
-#define LCD_CMD *(volatile unsigned char*)(LCD_BASE)
-#define LCD_DATA *(volatile unsigned char*)(LCD_BASE+1)
-
-static void lcd_send_command(unsigned cmd)
-{
- LCD_CMD = cmd;
-
- asm volatile (
- "nop \n\t"
- "nop \n\t"
- "nop \n\t"
- );
-}
-
-static void lcd_send_data(unsigned data)
-{
- LCD_DATA = (data & 0xff00) >> 8;
- LCD_DATA = (data & 0x00ff);
-}
-
-/* End of TCC77x specific defines */
-
-/* LCD init */
-void lcd_init_device(void)
-{
- uint32_t bus_width;
-
- /* Telechips init the same as the original firmware */
- bus_width = ((MCFG >> 11) & 0x3) ^ 3;
-
- CSCFG1 = (bus_width << 28) |
- (3 << 26) | /* MTYPE = 3 */
- ((LCD_BASE >> 28) << 22) | /* CSBASE = 0x5 */
- (1 << 20) | /* Unknown */
- (2 << 11) | /* Setup time = 2 cycles */
- (2 << 3) | /* Pulse width = 2+1 cycles */
- (2 << 0); /* Hold time = 2 cycle */
-
- GPIOE &= ~0x8;
- sleep(HZ/100); /* 10ms */
-
- GPIOE |= 0x08;
- sleep(HZ/100); /* 10ms */
-
- lcd_send_command(R_STANDBY_OFF);
- sleep(HZ/20); /* 50ms */
-
- lcd_send_command(R_OSCILLATION_MODE);
- lcd_send_command(0x01);
- sleep(HZ/100); /* 10ms */
-
- lcd_send_command(R_DCDC_AMP_ONOFF);
- lcd_send_command(0x01);
- sleep(HZ/100); /* 10ms */
-
- lcd_send_command(R_DCDC_AMP_ONOFF);
- lcd_send_command(0x09);
- sleep(HZ/100); /* 10ms */
-
- lcd_send_command(R_DCDC_AMP_ONOFF);
- lcd_send_command(0x0b);
- sleep(HZ/100); /* 10ms */
-
- lcd_send_command(R_DCDC_AMP_ONOFF);
- lcd_send_command(0x0f);
- sleep(HZ/100); /* 10ms */
-
- lcd_send_command(R_DCDC_SET);
- lcd_send_command(0x01);
- sleep(HZ/100); /* 10ms */
- sleep(HZ/10); /* 100ms */
-
- lcd_send_command(R_TEMP_COMPENSATION);
- lcd_send_command(0x01);
- sleep(HZ/100); /* 10ms */
-
- lcd_send_command(R_DRIVER_OUTPUT_MODE);
- lcd_send_command(0x03);
-
- lcd_send_command(R_ENTRY_MODE);
- lcd_send_command(0x81);
-
- lcd_send_command(R_N_LINE_INVERSION);
- lcd_send_command(0x04);
- lcd_send_command(0xfa);
- lcd_send_command(0x5f);
-
- lcd_set_contrast(0x28);
-
- lcd_send_command(R_SPEC_DISPLAY_PATTERN);
- lcd_send_command(0x0);
- sleep(HZ/100); /* 10ms */
-
- lcd_send_command(R_ADDRESSING_MODE);
- lcd_send_command(0x0);
- sleep(HZ/100); /* 10ms */
-
- lcd_send_command(R_PARTIAL_DISPLAY_MODE);
- lcd_send_command(0x0);
- sleep(HZ/100); /* 10ms */
-
- lcd_send_command(R_X_ADDR_AREA);
- lcd_send_command(0);
- lcd_send_command(0x80);
-
- lcd_send_command(R_Y_ADDR_AREA);
- lcd_send_command(0x0);
- lcd_send_command(0x80);
-
- lcd_send_command(R_DISPLAY_ON);
-
- lcd_send_command(R_SPEC_DISPLAY_PATTERN);
- lcd_send_command(0x0);
-
- /* Rockbox init */
- lcd_clear_display();
- lcd_update();
-}
-
-/*** hardware configuration ***/
-int lcd_default_contrast(void)
-{
- return 0x28;
-}
-
-void lcd_set_contrast(int val)
-{
- //val &= 0xFF;
- lcd_send_command(R_CONTRAST_CONTROL1);
- lcd_send_command(val);
-}
-
-void lcd_set_invert_display(bool yesno)
-{
- /* TODO: Implement lcd_set_invert_display() */
- (void)yesno;
-}
-
-/* turn the display upside down (call lcd_update() afterwards) */
-void lcd_set_flip(bool yesno)
-{
- lcd_send_command(R_DRIVER_OUTPUT_MODE);
- lcd_send_command(yesno ? 0x02 : 0x07);
-}
-
-/*** update functions ***/
-void lcd_yuv_set_options(unsigned options)
-{
- lcd_yuv_options = options;
-}
-
-/* TODO: implement me */
-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)
-{
- (void) src;
- (void) src_x;
- (void) src_y;
- (void) stride;
- (void) x;
- (void) y;
-
- return;
-
-}
-
-/* Update the display.
- This must be called after all other LCD functions that change the display. */
-void lcd_update(void)
-{
- lcd_update_rect(0, 0, LCD_WIDTH, LCD_HEIGHT);
-}
-
-/* Update a fraction of the display. */
-void lcd_update_rect(int x, int y, int width, int height)
-{
- const fb_data *addr;
-
- if (x + width >= LCD_WIDTH)
- width = LCD_WIDTH - x;
- if (y + height >= LCD_HEIGHT)
- height = LCD_HEIGHT - y;
-
- if ((width <= 0) || (height <= 0))
- return; /* Nothing left to do. */
-
- addr = FBADDR(x,y);
-
- if (width <= 1) {
- lcd_send_command(R_ENTRY_MODE); /* The X end address must be larger */
- lcd_send_command(0x80); /* that the X start address, so we */
- lcd_send_command(R_X_ADDR_AREA); /* switch to vertical mode for */
- lcd_send_command(x); /* single column updates and set */
- lcd_send_command(x + 1); /* the window width to 2 */
- } else {
- lcd_send_command(R_ENTRY_MODE);
- lcd_send_command(0x82);
- lcd_send_command(R_X_ADDR_AREA);
- lcd_send_command(x);
- lcd_send_command(x + width - 1);
- }
-
- lcd_send_command(R_Y_ADDR_AREA);
- lcd_send_command(y);
- lcd_send_command(y + height - 1);
-
- /* NOP needed because on some c200s, the previous lcd_send_command is
- interpreted as a separate command instead of part of R_Y_ADDR_AREA. */
- lcd_send_command(R_NOP);
-
- do {
- int w = width;
- do {
- lcd_send_data(*addr++);
- } while (--w > 0);
- addr += LCD_WIDTH - width;
- } while (--height > 0);
-}
diff --git a/firmware/target/arm/tcc77x/c100/power-c100.c b/firmware/target/arm/tcc77x/c100/power-c100.c
deleted file mode 100644
index e84ff1c852..0000000000
--- a/firmware/target/arm/tcc77x/c100/power-c100.c
+++ /dev/null
@@ -1,43 +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 "cpu.h"
-#include <stdbool.h>
-#include "kernel.h"
-#include "system.h"
-#include "power.h"
-
-void power_init(void)
-{
-}
-
-void ide_power_enable(bool on)
-{
-}
-
-bool ide_powered(void)
-{
- return true;
-}
-
-void power_off(void)
-{
-}
diff --git a/firmware/target/arm/tcc77x/crt0.S b/firmware/target/arm/tcc77x/crt0.S
deleted file mode 100644
index aebd8974da..0000000000
--- a/firmware/target/arm/tcc77x/crt0.S
+++ /dev/null
@@ -1,230 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2002 by Linus Nielsen Feltzing
- *
- * 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.
- *
- ****************************************************************************/
-
-/* Arm bootloader and startup code based on startup.s from the iPodLinux loader
- *
- * Copyright (c) 2003, Daniel Palffy (dpalffy (at) rainstorm.org)
- * Copyright (c) 2005, Bernard Leach <leachbj@bouncycastle.org>
- *
- */
-
-#include "config.h"
-#include "cpu.h"
-
- .section .init.text,"ax",%progbits
-
- .extern irq
- .extern fiq
- .extern UIE
- .extern main
-
- .global start
-
-/* Telechips firmware files start with a 32-byte header, as part of the code. */
-
-start:
-#ifdef TCCBOOT
- /* Add -DTCCBOOT to EXTRA_DEFINES in the bootloader Makefile to
- enable building the bootloader to be appended to the end of the
- original firmware, dual-booting based on a key-press.
-
- The following two values are filled in by mktccboot.
- */
-of_entrypoint: .word 0 /* Saved entrypoint of original firmware*/
-bl_entrypoint: .word 0 /* Location in RAM of the start of our bootloader */
-
-#else
-// ldr pc, =start_loc /* jump to the main entry point */
- b start_loc
-
- .word 0xffff0601 /* Unknown magic */
- .word 0x3a726556 /* "Ver:" */
- .word 0x31373030 /* "0071" */
- .word 0 /* First CRC32 */
- .word 0 /* Unknown - always 0 */
- .word 0 /* Second CRC32 */
- .word 0 /* length of firmware file */
-
-#if defined(LOGIK_DAX) || defined(SANSA_C100)
- /* Some original firmwares have 0x40 bytes of zeroes here - we
- don't know why, but err on the side of caution and include it
- here. */
- .space 0x40
-#endif
-#endif
-
-start_loc:
-
-#ifdef BOOTLOADER
-
-/*
- If we are appended to the OF (i.e. dual-booting), do a simple GPIO
- button check, and branch to the OF's entry point (saved by mktccboot)
- if not active
-*/
-
-#ifdef TCCBOOT
- mov r0, #0x80000000
-#if defined(LOGIK_DAX) || defined(SANSA_C100)
- ldr r0, [r0, #0x300] /* Hold button is GPIO A, pin 0x2 */
- tst r0, #0x2
-#elif defined(SANSA_M200)
- ldr r0, [r0, #0x310] /* Hold button is GPIO B, pin 0x200 */
- tst r0, #0x200
-#elif defined(IAUDIO_7)
- ldr r0, [r0, #0x300] /* Hold button is !GPIO A, pin 0x2 */
- tst r0, #0x2
-#else
- #error No bootup key detection implemented for this target
-#endif
-
- ldrne pc, of_entrypoint /* Jump to OF if HOLD button not pressed */
-#endif /* TCCBOOT */
-
-/* We are now definitely executing the bootloader, so we relocate to the
- linked address (see boot.lds) - 1MB from the end of DRAM.
-*/
-
-#ifdef TCCBOOT
- ldr r0, bl_entrypoint
-#else
- mov r0, #0x20000000 /* Otherwise, load address is the start of DRAM */
-#endif
- mov r1, #0x20000000 /* Destination: 1MB from end of DRAM */
- add r1, r1, #((MEMORYSIZE - 1) * 0x100000)
-
- ldr r2, =_dataend
-1:
- cmp r2, r1
- ldrhi r3, [r0], #4
- strhi r3, [r1], #4
- bhi 1b
-
- ldr pc, =copied_start /* jump to the relocated start_loc: */
-copied_start:
-#endif /* BOOTLOADER */
-
- /* Set up stack for IRQ mode */
- mov r0,#0xd2
- msr cpsr, r0
- ldr sp, =irq_stack
-
- /* Set up stack for FIQ mode */
- mov r0,#0xd1
- msr cpsr, r0
- ldr sp, =fiq_stack
-
-#ifndef BOOTLOADER
- /* Load the banked FIQ mode registers with useful values here.
- These values will be used in the FIQ handler in pcm-telechips.c */
- .equ DADO_BASE, 0x80000020
-
- ldr r10, =DADO_BASE
- ldr r11, =dma_play_data
-#endif
-
- /* Let svc, abort and undefined modes use irq stack */
- msr cpsr, #0xd3
- ldr sp, =irq_stack
- msr cpsr, #0xd7
- ldr sp, =irq_stack
- msr cpsr, #0xdb
- ldr sp, =irq_stack
-
- /* Switch to sys mode */
- mov r0,#0xdf
- msr cpsr, r0
- ldr sp, =stackend
-
- /* Copy exception handler code to address 0 */
- mov r2, #0x0
- ldr r3, =vectors_start
- ldr r4, =vectors_end
-1:
- cmp r4, r3
- ldrhi r5, [r3], #4
- strhi r5, [r2], #4
- bhi 1b
-
- /* Initialise bss section to zero */
- ldr r2, =_edata
- ldr r3, =_end
- mov r4, #0
-1:
- cmp r3, r2
- strhi r4, [r2], #4
- bhi 1b
-
- /* Set up some stack and munge it with 0xdeadbeef */
- ldr sp, =stackend
- mov r3, sp
- ldr r2, =stackbegin
- ldr r4, =0xdeadbeef
-1:
- cmp r3, r2
- strhi r4, [r2], #4
- bhi 1b
-
- bl main
- /* main() should never return */
-
-/* Exception handlers. Will be copied to address 0 after memory remapping */
-vectors_start:
- ldr pc, [pc, #24]
- ldr pc, [pc, #24]
- ldr pc, [pc, #24]
- ldr pc, [pc, #24]
- ldr pc, [pc, #24]
- ldr pc, [pc, #24]
- ldr pc, [pc, #24]
- ldr pc, [pc, #24]
-
- /* Exception vectors */
- .global vectors
-vectors:
- .word start
- .word undef_instr_handler
- .word software_int_handler
- .word prefetch_abort_handler
- .word data_abort_handler
- .word reserved_handler
- .word irq_handler
- .word fiq_handler
-vectors_end:
-
- .text
-
-irq_handler:
- stmfd sp!, {r0-r3, r12, lr}
- bl irq
- ldmfd sp!, {r0-r3, r12, lr}
- subs pc, lr, #4
-
-/* Align stacks to cache line boundary */
- .balign 16
-
-/* 256 words of IRQ stack */
- .space 256*4
-irq_stack:
-
-/* 256 words of FIQ stack */
- .space 256*4
-fiq_stack:
diff --git a/firmware/target/arm/tcc77x/debug-tcc77x.c b/firmware/target/arm/tcc77x/debug-tcc77x.c
deleted file mode 100644
index 5a97706722..0000000000
--- a/firmware/target/arm/tcc77x/debug-tcc77x.c
+++ /dev/null
@@ -1,77 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2007 by Rob Purchase
- *
- * 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 "cpu.h"
-#include "system.h"
-#include "string.h"
-#include <stdio.h>
-#include <stdbool.h>
-#include "button.h"
-#include "lcd.h"
-#include "font.h"
-#include "adc.h"
-
-bool dbg_ports(void)
-{
- return false;
-}
-
-bool dbg_hw_info(void)
-{
- int line = 0, i, button, oldline;
- bool done=false;
-
- lcd_setfont(FONT_SYSFIXED);
- lcd_clear_display();
-
- /* Put all the static text before the while loop */
- lcd_puts(0, line++, "[Hardware info]");
-
- line++;
- oldline=line;
- while(!done)
- {
- line = oldline;
- button = button_get(false);
-
- button &= ~BUTTON_REPEAT;
-#ifdef BUTTON_SELECT
- if (button == BUTTON_SELECT)
-#else
- if (button == BUTTON_STOP)
-#endif
- done=true;
-
- lcd_putsf(0, line++, "current tick: %08lx Seconds running: %08ld",
- current_tick, current_tick/HZ);
-
- lcd_putsf(0, line++, "GPIOA: 0x%08lx GPIOB: 0x%08lx", GPIOA, GPIOB);
- lcd_putsf(0, line++, "GPIOC: 0x%08lx GPIOD: 0x%08lx", GPIOC, GPIOD);
- lcd_putsf(0, line++, "GPIOE: 0x%08lx", GPIOE);
-
- for (i = 0; i<4; i++)
- lcd_putsf(0, line++, "ADC%d: 0x%04x", i, adc_read(i));
-
- lcd_update();
- }
- return false;
-}
diff --git a/firmware/target/arm/tcc77x/i2c-target.h b/firmware/target/arm/tcc77x/i2c-target.h
deleted file mode 100644
index 9b9a74b4d2..0000000000
--- a/firmware/target/arm/tcc77x/i2c-target.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2008 by Rob Purchase
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ****************************************************************************/
-#ifndef I2C_TARGET_H
-#define I2C_TARGET_H
-
-/* Definitions for the TCC77X I2C bus */
-
-#define SDA_BIT (1<<10)
-#define SCL_BIT (1<<11)
-
-#define SCL (GPIOB & SCL_BIT)
-#define SCL_HI GPIOB |= SCL_BIT
-#define SCL_LO GPIOB &= ~SCL_BIT
-
-#define SDA (GPIOB & SDA_BIT)
-#define SDA_HI GPIOB |= SDA_BIT
-#define SDA_LO GPIOB &= ~SDA_BIT
-#define SDA_INPUT GPIOB_DIR &= ~SDA_BIT
-#define SDA_OUTPUT GPIOB_DIR |= SDA_BIT
-
-#endif /* I2C_TARGET_H */
diff --git a/firmware/target/arm/tcc77x/iaudio7/ata2501.c b/firmware/target/arm/tcc77x/iaudio7/ata2501.c
deleted file mode 100644
index f7526b2b9a..0000000000
--- a/firmware/target/arm/tcc77x/iaudio7/ata2501.c
+++ /dev/null
@@ -1,110 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2008 Vitja Makarov
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ****************************************************************************/
-
-#include "config.h"
-#include "system.h"
-#include "cpu.h"
-#include "button.h"
-
-#include "ata2501.h"
-
-#define STB (1<<5)
-#define SDATA (1<<4)
-#define RESET (1<<6)
-#define SIFMD (1<<7)
-#define STB_DELAY 200
-
-static inline void ndelay(unsigned long nsecs)
-{
- nsecs /= 8;
- while (nsecs)
- nsecs--;
-}
-
-/*
- TODO: sensitivity
-*/
-void ata2501_init(void)
-{
- GPIOD_DIR |= (RESET | STB | SIFMD | (1 << 8) | (1 << 9));
- GPIOD_DIR &= ~SDATA;
-
- GPIOD &= ~STB;
- GPIOD |= (1 << 8) | SIFMD | (1 << 9);
-
- GPIOD &= ~RESET;
- ndelay(1000);
- GPIOD |= RESET;
-}
-
-unsigned short ata2501_read(void)
-{
- unsigned short ret = 0;
- int i;
-
- for (i = 0; i < 12; i++) {
- GPIOD |= STB;
- ndelay(100);
- ret <<= 1;
- if (GPIOD & SDATA)
- ret |= 1;
- GPIOD &= ~STB;
- ndelay(100);
- }
-
- return ret;
-}
-
-//#define ATA2501_TEST
-#ifdef ATA2501_TEST
-#include "lcd.h"
-
-static
-void bits(char *str, unsigned short val)
-{
- int i;
-
- for (i = 0; i < 12; i++)
- str[i] = (val & (1 << i)) ? '1' : '0';
- str[i] = 0;
-}
-
-void ata2501_test(void)
-{
- char buf[100];
- ata2501_init();
-
- while (1) {
- unsigned short data;
- int line = 0;
-
- data = ata2501_read();
- lcd_clear_display();
- lcd_puts(0, line++, "ATA2501 test");
-
- bits(buf, data);
- lcd_puts(0, line++, buf);
-
- lcd_update();
- sleep(HZ/10);
- }
-}
-#endif
diff --git a/firmware/target/arm/tcc77x/iaudio7/ata2501.h b/firmware/target/arm/tcc77x/iaudio7/ata2501.h
deleted file mode 100644
index 465d0b199c..0000000000
--- a/firmware/target/arm/tcc77x/iaudio7/ata2501.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2008 Vitja Makarov
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ****************************************************************************/
-#ifndef _ATA2501_H_
-#define _ATA2501_H_
-
-void ata2501_init(void);
-unsigned short ata2501_read(void);
-
-#endif /* _ATA2501_H_ */
diff --git a/firmware/target/arm/tcc77x/iaudio7/audio-iaudio7.c b/firmware/target/arm/tcc77x/iaudio7/audio-iaudio7.c
deleted file mode 100644
index bcb6843286..0000000000
--- a/firmware/target/arm/tcc77x/iaudio7/audio-iaudio7.c
+++ /dev/null
@@ -1,94 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2007 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"
-
-int audio_channels = 2;
-int audio_output_source = AUDIO_SRC_PLAYBACK;
-
-void audio_set_output_source(int source)
-{
- int oldmode = set_fiq_status(FIQ_DISABLED);
-
- if ((unsigned)source >= AUDIO_NUM_SOURCES)
- source = AUDIO_SRC_PLAYBACK;
-
- audio_output_source = source;
- set_fiq_status(oldmode);
-}
-
-void audio_input_mux(int source, unsigned flags)
-{
- static int last_source = AUDIO_SRC_PLAYBACK;
- static bool last_recording = false;
- bool recording = flags & SRCF_RECORDING;
-
- switch (source)
- {
- default: /* playback - no recording */
- source = AUDIO_SRC_PLAYBACK;
- case AUDIO_SRC_PLAYBACK:
- audio_channels = 2;
- if (source != last_source)
- {
- audiohw_set_monitor(false);
- /* audiohw_disable_recording();*/
- }
- break;
-
- case AUDIO_SRC_MIC: /* recording only */
- GPIOD |= 0x1;
-
- audio_channels = 1;
- if (source != last_source)
- {
- /*audiohw_set_monitor(false);
- audiohw_enable_recording(true); /. source mic */
- }
- break;
-
- case AUDIO_SRC_FMRADIO: /* recording and playback */
- GPIOD &= ~0x1;
-
- audio_channels = 2;
-
- if (source == last_source && recording == last_recording)
- break;
-
- last_recording = recording;
-
- if (recording)
- {
- /*audiohw_set_monitor(false);
- audiohw_enable_recording(false);*/
- }
- else
- {
- /*audiohw_disable_recording(); */
- audiohw_set_monitor(true); /* line 1 analog audio path */
- }
- break;
- } /* end switch */
-
- last_source = source;
-} /* audio_input_mux */
diff --git a/firmware/target/arm/tcc77x/iaudio7/backlight-target.h b/firmware/target/arm/tcc77x/iaudio7/backlight-target.h
deleted file mode 100644
index 0b227cd11c..0000000000
--- a/firmware/target/arm/tcc77x/iaudio7/backlight-target.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2008 Vitja Makarov
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ****************************************************************************/
-#ifndef BACKLIGHT_TARGET_H
-#define BACKLIGHT_TARGET_H
-
-#include <stdbool.h>
-#include "tcc77x.h"
-
-void power_touch_panel(bool on);
-
-static inline bool backlight_hw_init(void)
-{
- GPIOD_DIR |= 0x2;
- /* set backlight on by default, since the screen is unreadable without it */
- GPIOD |= 0x2;
- return true;
-}
-
-static inline void backlight_hw_on(void)
-{
- GPIOD |= 0x2;
- power_touch_panel(true);
-}
-
-static inline void backlight_hw_off(void)
-{
- GPIOD &= ~0x2;
- power_touch_panel(false);
-}
-#endif /* BACKLIGHT_TARGET_H */
diff --git a/firmware/target/arm/tcc77x/iaudio7/button-iaudio7.c b/firmware/target/arm/tcc77x/iaudio7/button-iaudio7.c
deleted file mode 100644
index abf31b4feb..0000000000
--- a/firmware/target/arm/tcc77x/iaudio7/button-iaudio7.c
+++ /dev/null
@@ -1,93 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2008 Vitja Makarov
- *
- * 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 "cpu.h"
-#include "button.h"
-#include "backlight.h"
-#include "adc.h"
-
-#include "button-target.h"
-#include "ata2501.h"
-
-void button_init_device(void)
-{
- ata2501_init();
-}
-
-/*
- touchpad:
- 0: stop
- 1-8: between next & prev
- 9: play
- 10: next
- 11: prev
-*/
-
-int button_read_device(void)
-{
- static bool hold_button = false;
- bool hold_button_old;
-
- int btn = BUTTON_NONE;
- int adc;
- int sensor;
-
- hold_button_old = hold_button;
- hold_button = button_hold();
-
-#ifndef BOOTLOADER
- if (hold_button != hold_button_old)
- backlight_hold_changed(hold_button);
-#endif
-
- if (button_hold())
- return BUTTON_NONE;
-
- adc = adc_read(0);
- sensor = ata2501_read();
-
- if (0 == (GPIOA & 4))
- btn |= BUTTON_POWER;
-
- /* seems they can't be hold together */
- if (adc < 0x120)
- btn |= BUTTON_VOLUP;
- else if (adc < 0x270)
- btn |= BUTTON_VOLDOWN;
- else if (adc < 0x300)
- btn |= BUTTON_MENU;
-
- if (sensor & (1 << 0))
- btn |= BUTTON_STOP;
- if (sensor & (1 << 9))
- btn |= BUTTON_PLAY;
- if (sensor & ((1 << 10) | 0x1c0))
- btn |= BUTTON_RIGHT;
- if (sensor & ((1 << 11) | 0xe))
- btn |= BUTTON_LEFT;
-
- return btn;
-}
-
-bool button_hold(void)
-{
- return !(GPIOA & 0x2);
-}
diff --git a/firmware/target/arm/tcc77x/iaudio7/button-target.h b/firmware/target/arm/tcc77x/iaudio7/button-target.h
deleted file mode 100644
index 9d232d9ae8..0000000000
--- a/firmware/target/arm/tcc77x/iaudio7/button-target.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2008 Vitja Makarov
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ****************************************************************************/
-#ifndef _IAUDIO7_BUTTON_TARGET_H_
-#define _IAUDIO7_BUTTON_TARGET_H_
-
-#define HAS_BUTTON_HOLD
-
-/* Main unit's buttons */
-#define BUTTON_POWER 0x00000001
-#define BUTTON_VOLUP 0x00000002
-#define BUTTON_VOLDOWN 0x00000004
-#define BUTTON_MENU 0x00000008
-
-#define BUTTON_LEFT 0x00000010
-#define BUTTON_RIGHT 0x00000020
-#define BUTTON_PLAY 0x00000040
-#define BUTTON_STOP 0x00000080
-
-#define BUTTON_ON BUTTON_POWER
-
-#define BUTTON_MAIN (BUTTON_POWER|BUTTON_VOLUP|BUTTON_VOLDOWN| \
- BUTTON_MENU|BUTTON_LEFT|BUTTON_RIGHT| \
- BUTTON_PLAY|BUTTON_STOP)
-
-/* Software power-off */
-#define POWEROFF_BUTTON BUTTON_POWER
-#define POWEROFF_COUNT 10
-
-#endif /* _IAUDIO7_BUTTON_TARGET_H_ */
diff --git a/firmware/target/arm/tcc77x/iaudio7/lcd-iaudio7.c b/firmware/target/arm/tcc77x/iaudio7/lcd-iaudio7.c
deleted file mode 100644
index e681e1eff7..0000000000
--- a/firmware/target/arm/tcc77x/iaudio7/lcd-iaudio7.c
+++ /dev/null
@@ -1,260 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2004 by Linus Nielsen Feltzing
- *
- * 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.
- *
- ****************************************************************************/
-
-/*
- Thanks Hein-Pieter van Braam for initial work.
-
- Mostly based on lcd-h300.c, adapted for the iaudio 7 by Vitja Makarov
- */
-
-#include <config.h>
-
-#include <kernel.h>
-#include <cpu.h>
-#include <lcd.h>
-#include <system-target.h>
-#include <panic.h>
-
-#include "hd66789r.h"
-
-static bool display_on = false; /* is the display turned on? */
-
-static inline void lcd_write_reg(int reg, int data)
-{
- GPIOA &= ~0x400;
- outw(0, 0x50010000);
- outw(reg << 1, 0x50010000);
- GPIOA |= 0x400;
-
- outw((data & 0xff00) >> 7, 0x50010008);
- outw((data << 24) >> 23, 0x50010008);
-}
-
-static void lcd_write_cmd(int reg)
-{
- GPIOA &= ~0x400;
- outw(0, 0x50010000);
- outw(reg << 1, 0x50010000);
- GPIOA |= 0x400;
-}
-
-/* Do what OF do */
-static void lcd_delay(int x)
-{
- int i;
-
- x *= 0xc35;
- for (i = 0; i < x * 8; i++) {
- }
-}
-
-
-static void _display_on(void)
-{
- GPIOA_DIR |= 0x8000 | 0x400;
- GPIOA |= 0x8000;
-
- /* power setup */
- lcd_write_reg(R_START_OSC, 0x0001);
- lcd_delay(0xf);
- lcd_write_reg(R_DISP_CONTROL1, 0x000);
- lcd_delay(0xa);
- lcd_write_reg(R_POWER_CONTROL2, 0x0002);
- lcd_write_reg(R_POWER_CONTROL3, 0x000a);
- lcd_write_reg(R_POWER_CONTROL4, 0xc5a);
- lcd_write_reg(R_POWER_CONTROL1, 0x0004);
- lcd_write_reg(R_POWER_CONTROL1, 0x0134);
- lcd_write_reg(R_POWER_CONTROL2, 0x0111);
- lcd_write_reg(R_POWER_CONTROL3, 0x001c);
- lcd_delay(0x28);
- lcd_write_reg(R_POWER_CONTROL4, 0x2c40);
- lcd_write_reg(R_POWER_CONTROL1, 0x0510);
- lcd_delay(0x3c);
-
- /* lcd init 2 */
- lcd_write_reg(R_DRV_OUTPUT_CONTROL, 0x0113);
- lcd_write_reg(R_DRV_WAVEFORM_CONTROL, 0x0700);
- lcd_write_reg(R_ENTRY_MODE, 0x1038);
- lcd_write_reg(R_DISP_CONTROL2, 0x0508); // 0x3c8, TMM
- lcd_write_reg(R_DISP_CONTROL3, 0x0000);
- lcd_write_reg(R_FRAME_CYCLE_CONTROL, 0x0003);
- lcd_write_reg(R_RAM_ADDR_SET, 0x0000);
- lcd_write_reg(R_GAMMA_FINE_ADJ_POS1, 0x0406);
- lcd_write_reg(R_GAMMA_FINE_ADJ_POS2, 0x0303);
- lcd_write_reg(R_GAMMA_FINE_ADJ_POS3, 0x0000);
- lcd_write_reg(R_GAMMA_GRAD_ADJ_POS, 0x0305);
- lcd_write_reg(R_GAMMA_FINE_ADJ_NEG1, 0x0404);
- lcd_write_reg(R_GAMMA_FINE_ADJ_NEG2, 0x0000);
- lcd_write_reg(R_GAMMA_FINE_ADJ_NEG3, 0x0000);
- lcd_write_reg(R_GAMMA_GRAD_ADJ_NEG, 0x0503);
- lcd_write_reg(R_GAMMA_AMP_ADJ_RES_POS, 0x1d05);
- lcd_write_reg(R_GAMMA_AMP_AVG_ADJ_RES_NEG, 0x1d05);
- lcd_write_reg(R_VERT_SCROLL_CONTROL, 0x0000);
- lcd_write_reg(R_1ST_SCR_DRV_POS, 0x9f00);
- lcd_write_reg(R_2ND_SCR_DRV_POS, 0x9f00);
- lcd_write_reg(R_HORIZ_RAM_ADDR_POS, 0x7f00);
- lcd_write_reg(R_VERT_RAM_ADDR_POS, 0x9f00);
-
- /* lcd init 3 */
- lcd_write_reg(R_POWER_CONTROL1, 0x4510);
- lcd_write_reg(R_DISP_CONTROL1, 0x0005);
- lcd_delay(0x28);
- lcd_write_reg(R_DISP_CONTROL1, 0x0025);
- lcd_write_reg(R_DISP_CONTROL1, 0x0027);
- lcd_delay(0x28);
- lcd_write_reg(R_DISP_CONTROL1, 0x0037);
-
- display_on = true;
-}
-
-void lcd_init_device(void)
-{
- /* Configure external memory banks */
- CSCFG1 = 0x0d500023 | tcc77x_cscfg_bw(TCC77X_CSCFG_BW16);
-
- /* may be reset */
- GPIOA |= 0x8000;
-
- _display_on();
-}
-
-void lcd_enable(bool on)
-{
- if (display_on == on)
- return;
-
- if (on) {
- _display_on();
- send_event(LCD_EVENT_ACTIVATION, NULL);
- } else {
- /** Off sequence according to datasheet, p. 130 **/
- lcd_write_reg(R_FRAME_CYCLE_CONTROL, 0x0002); /* EQ=0, 18 clks/line */
- lcd_write_reg(R_DISP_CONTROL1, 0x0036); /* GON=1, DTE=1, REV=1, D1-0=10 */
- sleep(2);
-
- lcd_write_reg(R_DISP_CONTROL1, 0x0026); /* GON=1, DTE=0, REV=1, D1-0=10 */
- sleep(2);
-
- lcd_write_reg(R_DISP_CONTROL1, 0x0000); /* GON=0, DTE=0, D1-0=00 */
-
- lcd_write_reg(R_POWER_CONTROL1, 0x0000); /* SAP2-0=000, AP2-0=000 */
- lcd_write_reg(R_POWER_CONTROL3, 0x0000); /* PON=0 */
- lcd_write_reg(R_POWER_CONTROL4, 0x0000); /* VCOMG=0 */
-
- /* datasheet p. 131 */
- lcd_write_reg(R_POWER_CONTROL1, 0x0001); /* STB=1: standby mode */
-
- display_on = false;
- }
-}
-
-bool lcd_active(void)
-{
- return display_on;
-}
-
-
-#define RGB(r,g,b) ((((r)&0x3f) << 12)|(((g)&0x3f) << 6)|(((b)&0x3f)))
-
-
-void lcd_update(void)
-{
- lcd_update_rect(0, 0, LCD_WIDTH, LCD_HEIGHT);
-}
-
-/* todo: need tests */
-void lcd_update_rect(int sx, int sy, int width, int height)
-{
- int x, y;
-
- if (!display_on)
- return;
-
- if (width <= 0 || height <= 0) /* nothing to do */
- return;
-
- width += sx;
- height += sy;
-
- if (width > LCD_WIDTH)
- width = LCD_WIDTH;
- if (height > LCD_HEIGHT)
- height = LCD_HEIGHT;
-
- lcd_write_reg(R_ENTRY_MODE, 0x1028);
- /* set update window */
- lcd_write_reg(R_HORIZ_RAM_ADDR_POS, (LCD_HEIGHT - 1) << 8);
- lcd_write_reg(R_VERT_RAM_ADDR_POS, ((width - 1) << 8) | sx);
- lcd_write_reg(R_RAM_ADDR_SET, (sx << 8) | (LCD_HEIGHT - sy - 1));
- lcd_write_cmd(R_WRITE_DATA_2_GRAM);
-
- for (y = sy; y < height; y++) {
- for (x = sx; x < width; x++) {
- fb_data c;
- unsigned long color;
-
- c = *FBADDR(x,y);
- color =
- ((c & 0x1f) << 1) | ((c & 0x7e0) << 1) | ((c & 0xf800) <<
- 2);
-
- /* TODO: our color is 18-bit */
- outw((color >> 9) & 0x1ff, 0x50010008);
- outw((color) & 0x1ff, 0x50010008);
- }
- }
-}
-
-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;
-}
-
-/* TODO: implement me */
-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)
-{
- (void) src;
- (void) src_x;
- (void) src_y;
- (void) stride;
- (void) x;
- (void) y;
-
- if (!display_on)
- return;
-
- width &= ~1; /* stay on the safe side */
- height &= ~1;
-
- panicf("%s", __func__);
-}
diff --git a/firmware/target/arm/tcc77x/iaudio7/power-iaudio7.c b/firmware/target/arm/tcc77x/iaudio7/power-iaudio7.c
deleted file mode 100644
index baf93b73aa..0000000000
--- a/firmware/target/arm/tcc77x/iaudio7/power-iaudio7.c
+++ /dev/null
@@ -1,149 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2008 Vitja Makarov
- *
- * 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 <stdbool.h>
-
-#include "config.h"
-#include "cpu.h"
-#include "kernel.h"
-#include "system.h"
-#include "power.h"
-#include "backlight-target.h"
-
-#include "pcf50606.h"
-
-void power_init(void)
-{
- pcf50606_write(PCF5060X_DCDC1, 0x90);
- pcf50606_write(PCF5060X_DCDC2, 0x48);
- pcf50606_write(PCF5060X_DCDC3, 0xfc);
- pcf50606_write(PCF5060X_DCDC4, 0xb1);
-
- pcf50606_write(PCF5060X_IOREGC, 0xe9);
- /* 3.3V, touch-panel */
- pcf50606_write(PCF5060X_D1REGC1, 0xf8);
- pcf50606_write(PCF5060X_D2REGC1, 0xf2);
- pcf50606_write(PCF5060X_D3REGC1, 0xf5);
-
- pcf50606_write(PCF5060X_LPREGC1, 0x00);
- pcf50606_write(PCF5060X_LPREGC2, 0x02);
-
- pcf50606_write(PCF5060X_DCUDC1, 0xe6);
- pcf50606_write(PCF5060X_DCUDC2, 0x30);
-
- pcf50606_write(PCF5060X_DCDEC1, 0xe7);
- pcf50606_write(PCF5060X_DCDEC2, 0x02);
-
- pcf50606_write(PCF5060X_INT1M, 0x5b);
- pcf50606_write(PCF5060X_INT1M, 0xaf);
- pcf50606_write(PCF5060X_INT1M, 0x8f);
-
- pcf50606_write(PCF5060X_OOCC1, 0x40);
- pcf50606_write(PCF5060X_OOCC2, 0x05);
-
- pcf50606_write(PCF5060X_MBCC3, 0x3a);
- pcf50606_write(PCF5060X_GPOC1, 0x00);
- pcf50606_write(PCF5060X_BBCC, 0xf8);
-}
-
-/* Control leds on ata2501 board */
-void power_touch_panel(bool on)
-{
- if (on)
- pcf50606_write(PCF5060X_D1REGC1, 0xf8);
- else
- pcf50606_write(PCF5060X_D1REGC1, 0x00);
-}
-
-void ide_power_enable(bool on)
-{
- (void) on;
-}
-
-bool ide_powered(void)
-{
- return true;
-}
-
-void power_off(void)
-{
- /* Forcibly cut power to SoC & peripherals by putting the PCF to sleep */
- pcf50606_write(PCF5060X_OOCC1, GOSTDBY | CHGWAK | EXTONWAK);
-}
-
-#if CONFIG_TUNER
-#include "tuner.h"
-
-/** Tuner **/
-static bool powered = false;
-
-#define TUNNER_CLK (1 << 5)
-#define TUNNER_DATA (1 << 6)
-#define TUNNER_NR_W (1 << 7)
-
-bool tuner_power(bool status)
-{
- bool old_status;
- lv24020lp_lock();
-
- old_status = powered;
-
- if (status != old_status)
- {
- if (status)
- {
- /* When power up, host should initialize the 3-wire bus
- in host read mode: */
-
- /* 1. Set direction of the DATA-line to input-mode. */
- GPIOA_DIR &= ~TUNNER_DATA;
-
- /* 2. Drive NR_W low */
- GPIOA &= ~TUNNER_NR_W;
- GPIOA_DIR |= TUNNER_NR_W;
-
- /* 3. Drive CLOCK high */
- GPIOA |= TUNNER_CLK;
- GPIOA_DIR |= TUNNER_CLK;
-
- lv24020lp_power(true);
- }
- else
- {
- lv24020lp_power(false);
-
- /* set all as inputs */
- GPIOC_DIR &= ~(TUNNER_CLK | TUNNER_DATA | TUNNER_NR_W);
- }
-
- powered = status;
- }
-
- lv24020lp_unlock();
- return old_status;
-}
-
-#endif /* CONFIG_TUNER */
-
-unsigned int power_input_status(void)
-{
- return (GPIOA & 0x1) ?
- POWER_INPUT_MAIN_CHARGER : POWER_INPUT_NONE;
-}
diff --git a/firmware/target/arm/tcc77x/iaudio7/powermgmt-iaudio7.c b/firmware/target/arm/tcc77x/iaudio7/powermgmt-iaudio7.c
deleted file mode 100644
index bc7ead61f0..0000000000
--- a/firmware/target/arm/tcc77x/iaudio7/powermgmt-iaudio7.c
+++ /dev/null
@@ -1,84 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id: powermgmt-cowond2.c 17847 2008-06-28 18:10:04Z bagder $
- *
- * Copyright (C) 2007 by Karl Kurbjun
- *
- * 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 "adc.h"
-#include "powermgmt.h"
-#include "kernel.h"
-#include "pcf50606.h"
-
-unsigned short current_voltage = 5150;
-
-const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] =
-{
- /* FIXME: calibrate value */
- 4400
-};
-
-const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] =
-{
- /* FIXME: calibrate value */
- 4600
-};
-
-/* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */
-const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11] =
-{
- /* FIXME: calibrate values. Table is "inherited" from iPod-PCF / H100 */
- { 4500, 4810, 4910, 4970, 5030, 5070, 5120, 5140, 5170, 5250, 5400 }
-};
-
-#if CONFIG_CHARGING
-/* voltages (millivolt) of 0%, 10%, ... 100% when charging enabled */
-const unsigned short percent_to_volt_charge[11] =
-{
- /* FIXME: calibrate values. Table is "inherited" from iPod-PCF / H100 */
- 4760, 5440, 5510, 5560, 5610, 5640, 5660, 5760, 5820, 5840, 5850 /* NiMH */
-};
-#endif /* CONFIG_CHARGING */
-
-#define BATTERY_SCALE_FACTOR 6000
-/* full-scale ADC readout (2^10) in millivolt */
-
-/* Returns battery voltage from ADC [millivolts] */
-int _battery_voltage(void)
-{
- static unsigned last_tick = 0;
-
- if (0 == last_tick || TIME_BEFORE(last_tick+HZ, current_tick))
- {
- int adc_val, irq_status;
- unsigned char buf[2];
-
- irq_status = disable_irq_save();
- pcf50606_write(PCF5060X_ADCC2, 0x1);
- pcf50606_read_multiple(PCF5060X_ADCS1, buf, 2);
- restore_interrupt(irq_status);
-
- adc_val = (buf[0]<<2) | (buf[1] & 3); //ADCDAT1H+ADCDAT1L
- current_voltage = (adc_val * BATTERY_SCALE_FACTOR) >> 10;
-
- last_tick = current_tick;
- }
-
- return current_voltage;
-}
-
diff --git a/firmware/target/arm/tcc77x/logikdax/adc-target.h b/firmware/target/arm/tcc77x/logikdax/adc-target.h
deleted file mode 100644
index 1916d93598..0000000000
--- a/firmware/target/arm/tcc77x/logikdax/adc-target.h
+++ /dev/null
@@ -1,28 +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.
- *
- ****************************************************************************/
-#ifndef _ADC_TARGET_H_
-#define _ADC_TARGET_H_
-
-#define NUM_ADC_CHANNELS 8
-
-#define ADC_BUTTONS 0
-
-#endif /* _ADC_TARGET_H_ */
diff --git a/firmware/target/arm/tcc77x/logikdax/audio-logikdax.c b/firmware/target/arm/tcc77x/logikdax/audio-logikdax.c
deleted file mode 100644
index 90c9a68827..0000000000
--- a/firmware/target/arm/tcc77x/logikdax/audio-logikdax.c
+++ /dev/null
@@ -1,40 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2007 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"
-
-int audio_channels = 2;
-int audio_output_source = AUDIO_SRC_PLAYBACK;
-
-#if INPUT_SRC_CAPS != 0
-void audio_set_output_source(int source)
-{
- (void)source;
-}
-
-void audio_input_mux(int source, unsigned flags)
-{
- (void)source;
- (void)flags;
-}
-#endif
diff --git a/firmware/target/arm/tcc77x/logikdax/button-logikdax.c b/firmware/target/arm/tcc77x/logikdax/button-logikdax.c
deleted file mode 100644
index abf939753a..0000000000
--- a/firmware/target/arm/tcc77x/logikdax/button-logikdax.c
+++ /dev/null
@@ -1,100 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2007 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 "cpu.h"
-#include "button.h"
-#include "adc.h"
-
-/*
-
-Results of button testing:
-
-HOLD: GPIOA & 0x0002 (0=pressed, 0x0002 = released)
-POWER: GPIOA & 0x8000 (0=pressed, 0x8000 = released)
-
-ADC[0]: (approx values)
-
-RIGHT - 0x37
-LEFT - 0x7f
-JOYSTICK PRESS - 0xc7
-UP - 0x11e
-DOWN - 0x184
-MODE - 0x1f0/0x1ff
-PRESET - 0x268/0x269
-REC - 0x2dd
-
-Values of ADC[0] tested in OF disassembly: 0x50, 0x96, 0xdc, 0x208, 0x384
-
-*/
-
-void button_init_device(void)
-{
- /* Nothing to do */
-}
-
-int button_read_device(void)
-{
- int btn = BUTTON_NONE;
- int adc;
-
- adc = adc_read(ADC_BUTTONS);
-
- if (adc < 0x384) {
- if (adc < 0x140) {
- if (adc < 0x96) {
- if (adc < 0x50) {
- btn |= BUTTON_RIGHT; /* 0x00..0x4f */
- } else {
- btn |= BUTTON_LEFT; /* 0x50..0x95 */
- }
- } else {
- if (adc < 0xe0) {
- btn |= BUTTON_SELECT; /* 0x96..0xdf */
- } else {
- btn |= BUTTON_UP; /* 0xe0..0x13f */
- }
- }
- } else {
- if (adc < 0x208) {
- if (adc < 0x1b0) {
- btn |= BUTTON_DOWN; /* 0x140..0x1af */
- } else {
- btn |= BUTTON_MODE; /* 0x1b0..0x207 */
- }
- } else {
- if (adc < 0x290) {
- btn |= BUTTON_PRESET; /* 0x208..0x28f */
- } else {
- btn |= BUTTON_REC; /* 0x290..0x383 */
- }
- }
- }
- }
-
- if (!(GPIOA & 0x2))
- btn |= BUTTON_HOLD;
-
- if (!(GPIOA & 0x8000))
- btn |= BUTTON_POWERPLAY;
-
- return btn;
-}
diff --git a/firmware/target/arm/tcc77x/logikdax/button-target.h b/firmware/target/arm/tcc77x/logikdax/button-target.h
deleted file mode 100644
index 505fe438ed..0000000000
--- a/firmware/target/arm/tcc77x/logikdax/button-target.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2007 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.
- *
- ****************************************************************************/
-
-#ifndef _BUTTON_TARGET_H_
-#define _BUTTON_TARGET_H_
-
-/* Main unit's buttons */
-#define BUTTON_POWERPLAY 0x00000001
-#define BUTTON_MODE 0x00000002
-#define BUTTON_HOLD 0x00000004
-#define BUTTON_REC 0x00000008
-#define BUTTON_PRESET 0x00000010
-#define BUTTON_LEFT 0x00000020
-#define BUTTON_RIGHT 0x00000040
-#define BUTTON_UP 0x00000080
-#define BUTTON_DOWN 0x00000100
-#define BUTTON_SELECT 0x00000200
-
-#define BUTTON_MAIN (BUTTON_POWERPLAY|BUTTON_MODE|BUTTON_HOLD\
- |BUTTON_REC|BUTTON_PRESET|BUTTON_LEFT\
- |BUTTON_RIGHT|BUTTON_UP|BUTTON_DOWN|BUTTON_SELECT)
-
-/* Software power-off */
-#define POWEROFF_BUTTON BUTTON_POWERPLAY
-#define POWEROFF_COUNT 40
-
-#endif /* _BUTTON_TARGET_H_ */
diff --git a/firmware/target/arm/tcc77x/m200/adc-target.h b/firmware/target/arm/tcc77x/m200/adc-target.h
deleted file mode 100644
index e9640cc9da..0000000000
--- a/firmware/target/arm/tcc77x/m200/adc-target.h
+++ /dev/null
@@ -1,28 +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.
- *
- ****************************************************************************/
-#ifndef _ADC_TARGET_H_
-#define _ADC_TARGET_H_
-
-#define NUM_ADC_CHANNELS 8
-
-#define ADC_BUTTONS 1
-
-#endif /* _ADC_TARGET_H_ */
diff --git a/firmware/target/arm/tcc77x/m200/audio-m200.c b/firmware/target/arm/tcc77x/m200/audio-m200.c
deleted file mode 100644
index 107b2743ae..0000000000
--- a/firmware/target/arm/tcc77x/m200/audio-m200.c
+++ /dev/null
@@ -1,38 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2007 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"
-
-int audio_channels = 2;
-int audio_output_source = AUDIO_SRC_PLAYBACK;
-
-void audio_set_output_source(int source)
-{
- (void)source;
-}
-
-void audio_input_mux(int source, unsigned flags)
-{
- (void)source;
- (void)flags;
-}
diff --git a/firmware/target/arm/tcc77x/m200/backlight-target.h b/firmware/target/arm/tcc77x/m200/backlight-target.h
deleted file mode 100644
index d73ac78852..0000000000
--- a/firmware/target/arm/tcc77x/m200/backlight-target.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2008 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.
- *
- ****************************************************************************/
-#ifndef BACKLIGHT_TARGET_H
-#define BACKLIGHT_TARGET_H
-
-#include "tcc77x.h"
-
-static inline bool backlight_hw_init(void)
-{
- GPIOA_DIR |= 0x40;
- return true;
-}
-
-static inline void backlight_hw_on(void)
-{
- /* Enable backlight */
- GPIOA |= 0x40;
-}
-
-static inline void backlight_hw_off(void)
-{
- /* Disable backlight */
- GPIOA &= ~0x40;
-}
-
-#endif
diff --git a/firmware/target/arm/tcc77x/m200/button-m200.c b/firmware/target/arm/tcc77x/m200/button-m200.c
deleted file mode 100644
index a37fe1302c..0000000000
--- a/firmware/target/arm/tcc77x/m200/button-m200.c
+++ /dev/null
@@ -1,99 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2007 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 "cpu.h"
-#include "button.h"
-#include "adc.h"
-
-/*
-
-Results of button testing (viewing ADC values whilst pressing buttons):
-
-HOLD: GPIOB & 0x0200 (0=hold active, 0x0200 = hold inactive)
-
-ADC[1]: (approx values)
-
-Idle - 0x3ff
-MENU - unknown
-
-REPEAT/AB - 0x03?
-LEFT - 0x07?-0x08?
-SELECT - 0x0c?
-RIGHT - 0x11?
-
-PLAY/PAUSE - 0x17?-0x018?
-VOL UP - 0x1e?-0x01f?
-VOL DOWN - 0x26?
-
-*/
-
-void button_init_device(void)
-{
- /* Nothing to do */
-}
-
-int button_read_device(void)
-{
- int btn = BUTTON_NONE;
- int adc;
-
- /* TODO - determine how to detect BUTTON_MENU - it doesn't appear to
- be connected to a GPIO or to an ADC
- */
-
- adc = adc_read(ADC_BUTTONS);
-
- if (adc < 0x384) {
- if (adc < 0x140) {
- if (adc < 0x96) {
- if (adc < 0x50) {
- btn |= BUTTON_REPEATAB; /* 0x00..0x4f */
- } else {
- btn |= BUTTON_LEFT; /* 0x50..0x95 */
- }
- } else {
- if (adc < 0xe0) {
- btn |= BUTTON_SELECT; /* 0x96..0xdf */
- } else {
- btn |= BUTTON_RIGHT; /* 0xe0..0x13f */
- }
- }
- } else {
- if (adc < 0x208) {
- if (adc < 0x1b0) {
- btn |= BUTTON_PLAYPAUSE; /* 0x140..0x1af */
- } else {
- btn |= BUTTON_VOLUP; /* 0x1b0..0x207 */
- }
- } else {
- btn |= BUTTON_VOLDOWN; /* 0x209..0x383 */
- }
- }
- }
-
- return btn;
-}
-
-bool button_hold(void)
-{
- return (GPIOB & 0x200)?false:true;
-}
diff --git a/firmware/target/arm/tcc77x/m200/button-target.h b/firmware/target/arm/tcc77x/m200/button-target.h
deleted file mode 100644
index b96df22edc..0000000000
--- a/firmware/target/arm/tcc77x/m200/button-target.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2007 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.
- *
- ****************************************************************************/
-
-#ifndef _BUTTON_TARGET_H_
-#define _BUTTON_TARGET_H_
-
-#define HAS_BUTTON_HOLD
-
-/* Main unit's buttons */
-#define BUTTON_MENU 0x00000001
-#define BUTTON_VOLUP 0x00000002
-#define BUTTON_VOLDOWN 0x00000004
-#define BUTTON_PLAYPAUSE 0x00000008
-#define BUTTON_REPEATAB 0x00000010
-#define BUTTON_LEFT 0x00000020
-#define BUTTON_RIGHT 0x00000040
-#define BUTTON_SELECT 0x00000080
-
-#define BUTTON_MAIN (BUTTON_MENU|BUTTON_VOLUP|BUTTON_VOLDOWN\
- |BUTTON_PLAYPAUSE|BUTTON_REPEATAB|BUTTON_LEFT\
- |BUTTON_RIGHT|BUTTON_SELECT)
-
-/* Software power-off */
-#define POWEROFF_BUTTON BUTTON_MENU
-#define POWEROFF_COUNT 40
-
-#endif /* _BUTTON_TARGET_H_ */
diff --git a/firmware/target/arm/tcc77x/m200/power-m200.c b/firmware/target/arm/tcc77x/m200/power-m200.c
deleted file mode 100644
index e84ff1c852..0000000000
--- a/firmware/target/arm/tcc77x/m200/power-m200.c
+++ /dev/null
@@ -1,43 +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 "cpu.h"
-#include <stdbool.h>
-#include "kernel.h"
-#include "system.h"
-#include "power.h"
-
-void power_init(void)
-{
-}
-
-void ide_power_enable(bool on)
-{
-}
-
-bool ide_powered(void)
-{
- return true;
-}
-
-void power_off(void)
-{
-}
diff --git a/firmware/target/arm/tcc77x/powermgmt-tcc77x.c b/firmware/target/arm/tcc77x/powermgmt-tcc77x.c
deleted file mode 100644
index 07a56777d0..0000000000
--- a/firmware/target/arm/tcc77x/powermgmt-tcc77x.c
+++ /dev/null
@@ -1,66 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2007 by Karl Kurbjun
- *
- * 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 "adc.h"
-#include "powermgmt.h"
-#include "kernel.h"
-#include "pcf50606.h"
-
-unsigned short current_voltage = 3910;
-
-const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] =
-{
- /* FIXME: calibrate value */
- 3380
-};
-
-const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] =
-{
- /* FIXME: calibrate value */
- 3300
-};
-
-/* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */
-const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11] =
-{
- /* FIXME: calibrate values. Table is "inherited" from iPod-PCF / H100 */
- { 3370, 3650, 3700, 3740, 3780, 3820, 3870, 3930, 4000, 4080, 4160 }
-};
-
-#if CONFIG_CHARGING
-/* voltages (millivolt) of 0%, 10%, ... 100% when charging enabled */
-const unsigned short percent_to_volt_charge[11] =
-{
- /* FIXME: calibrate values. Table is "inherited" from iPod-PCF / H100 */
- 3370, 3650, 3700, 3740, 3780, 3820, 3870, 3930, 4000, 4080, 4160
-};
-#endif /* CONFIG_CHARGING */
-
-#define BATTERY_SCALE_FACTOR 6000
-/* full-scale ADC readout (2^10) in millivolt */
-
-/* Returns battery voltage from ADC [millivolts] */
-int _battery_voltage(void)
-{
- return current_voltage;
-}
-
diff --git a/firmware/target/arm/tcc77x/system-target.h b/firmware/target/arm/tcc77x/system-target.h
deleted file mode 100644
index beeeac05c6..0000000000
--- a/firmware/target/arm/tcc77x/system-target.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2007 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.
- *
- ****************************************************************************/
-#ifndef SYSTEM_TARGET_H
-#define SYSTEM_TARGET_H
-
-#include "system-arm.h"
-
-#define CPUFREQ_DEFAULT 98784000
-#define CPUFREQ_NORMAL 98784000
-#define CPUFREQ_MAX 120000000
-
-#define inl(a) (*(volatile unsigned long *) (a))
-#define outl(a,b) (*(volatile unsigned long *) (b) = (a))
-#define inb(a) (*(volatile unsigned char *) (a))
-#define outb(a,b) (*(volatile unsigned char *) (b) = (a))
-#define inw(a) (*(volatile unsigned short *) (a))
-#define outw(a,b) (*(volatile unsigned short *) (b) = (a))
-
-/* TC32 is configured to 1MHz in clock_init() */
-#define USEC_TIMER TC32MCNT
-
-static inline void udelay(unsigned usecs)
-{
- unsigned stop = USEC_TIMER + usecs;
- while (TIME_BEFORE(USEC_TIMER, stop));
-}
-
-
-#define TCC77X_CSCFG_BW8 0
-#define TCC77X_CSCFG_BW16 1
-
-/* Due to hardware bug or "feature" this hack is needed to set bus width bits */
-static inline
-unsigned long tcc77x_cscfg_bw(int bw) {
- if (bw == TCC77X_CSCFG_BW8)
- return (((MCFG >> 11) & 3) ^ 3) << 28;
- else
- return (((MCFG >> 11) & 3) ^ 2) << 28;
-}
-
-#endif /* SYSTEM_TARGET_H */
diff --git a/firmware/target/arm/tcc77x/system-tcc77x.c b/firmware/target/arm/tcc77x/system-tcc77x.c
deleted file mode 100644
index cffb4deba4..0000000000
--- a/firmware/target/arm/tcc77x/system-tcc77x.c
+++ /dev/null
@@ -1,326 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2007 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 "kernel.h"
-#include "system.h"
-#include "panic.h"
-
-/* Externally defined interrupt handlers */
-extern void TIMER(void);
-extern void ADC(void);
-extern void USB_DEVICE(void);
-
-void irq(void)
-{
- int irq = IREQ & 0x7fffffff;
- CREQ = irq; /* Clears the corresponding IRQ status */
-
- if (irq & TIMER0_IRQ_MASK)
- TIMER();
- else if (irq & ADC_IRQ_MASK)
- ADC();
-#ifdef HAVE_USBSTACK
- else if (irq & USBD_IRQ_MASK)
- USB_DEVICE();
-#endif
- else
- panicf("Unhandled IRQ 0x%08X", irq);
-}
-
-void fiq_handler(void) __attribute__((interrupt ("FIQ"), naked));
-
-#ifdef BOOTLOADER
-void fiq_handler(void)
-{
- /* TODO */
-}
-#endif
-
-void system_reboot(void)
-{
-}
-
-void system_exception_wait(void)
-{
- while (1);
-}
-
-/* TODO - these should live in the target-specific directories and
- once we understand what all the GPIO pins do, move the init to the
- specific driver for that hardware. For now, we just perform the
- same GPIO init as the original firmware - this makes it easier to
- investigate what the GPIO pins do.
-*/
-
-#ifdef LOGIK_DAX
-static void gpio_init(void)
-{
- /* Do what the original firmware does */
- GPIOD_FUNC = 0;
- GPIOD_DIR = 0x3f0;
- GPIOD = 0xe0;
-
- GPIOE_FUNC = 0;
- GPIOE_DIR = 0xe0;
- GPIOE = 0;
-
- GPIOA_FUNC = 0;
- GPIOA_DIR = 0xffff1000; /* 0 - 0xf000 */
- GPIOA = 0x1080;
-
- GPIOB_FUNC = 0x16a3;
- GPIOB_DIR = 0x6ffff;
- GPIOB = 0;
-
- GPIOC_FUNC = 1;
- GPIOC_DIR = 0x03ffffff; /* mvn r2, 0xfc000000 */
- GPIOC = 0;
-}
-#elif defined(IAUDIO_7)
-static void gpio_init(void)
-{
- /* Do what the original firmware does */
- GPIOA_FUNC = 0;
- GPIOB_FUNC = 0x1623;
- GPIOC_FUNC = 1;
- GPIOD_FUNC = 0;
- GPIOE_FUNC = 0;
- GPIOA = 0x30;
- GPIOB = 0x00c00;
- GPIOC = 0;
- GPIOD = 0x180;
- GPIOE = 0x80;
- GPIOA_DIR = 0x84b0;
- GPIOB_DIR = 0x80c00;
- GPIOC_DIR = 0x2000000;
- GPIOD_DIR = 0x3e3;
- GPIOE_DIR = 0x88;
-}
-#elif defined(SANSA_M200)
-static void gpio_init(void)
-{
- /* TODO - Implement for M200 */
-}
-#elif defined(SANSA_C100)
-static void gpio_init(void)
-{
- /* Do what the original firmware does */
- GPIOA_FUNC = 0;
- GPIOB_FUNC = 0x16A3;
- GPIOC_FUNC = 1;
- GPIOD_FUNC |= 2;
- GPIOE_FUNC = 0;
-
- GPIOA_DIR = 0xFFFF0E00;
- GPIOB_DIR = 0x6FFFF;
- GPIOC_DIR = 0x03FFFFFF;
- GPIOD_DIR = 0x3F7;
- GPIOE_DIR = 0x9B;
-
- GPIOA = 0x80;
- GPIOB = 0;
- GPIOC = 0;
- GPIOD |= 0xC0;
- GPIOE = 0x9B;
-}
-#endif
-
-/* Second function called in the original firmware's startup code - we just
- set up the clocks in the same way as the original firmware for now. */
-static void clock_init(void)
-{
- unsigned int i;
-
- /* STP = 0x1, PW = 0x04 , HLD = 0x0 */
- CSCFG3 = (CSCFG3 &~ 0x3fff) | 0x820;
-
- /* XIN=External main, Fcpu=Fsys, BCKDIV=1 (Fbus = Fsys / 2) */
- CLKCTRL = (CLKCTRL & ~0xff) | 0x14;
-
- if (BMI & 0x20)
- PCLKCFG0 = 0xc82d7000; /* EN1 = 1, XIN=Ext. main, DIV1 = 0x2d, P1 = 1 */
- else
- PCLKCFG0 = 0xc8ba7000; /* EN1 = 1, XIN=Ext. main, DIV1 = 0xba, P1 = 1 */
-
- MCFG |= 0x2000;
-
-#ifdef LOGIK_DAX
- /* Only seen in the Logik DAX original firmware */
- SDCFG = (SDCFG & ~0x7000) | 0x2000;
-#endif
-
- /* Disable PLL */
- PLL0CFG |= 0x80000000;
-
- /* Enable PLL, M=0xcf, P=0x13. m=M+8, p=P+2, S = 0
- Fout = (215/21)*12MHz = 122857142Hz */
- PLL0CFG = 0x0000cf13;
-
- i = 8000;
- while (--i) {};
-
- /* Enable PLL0 */
- CLKDIVC = 0x81000000;
-
- /* Fsys = PLL0, Fcpu = Fsys, Fbus=Fsys / 2 */
- CLKCTRL = 0x80000010;
-
- asm volatile (
- "nop \n\t"
- "nop \n\t"
- );
-
- /* Enable Z-Clock */
- PCLKCFG5 |= (1<<31) | (4<<28); /* Timer Z-Clock enable, XIN direct*/
-
- /* Set TC32 timer to be our USEC_TIMER (Xin divided by 12 = 1MHz) */
- TC32MCNT = 0;
- TC32LDV = 0;
- TC32EN = (1<<24) | 11;
-}
-
-static void cpu_init(void)
-{
- /* Memory protection - see page 48 of ARM946 TRM
-http://infocenter.arm.com/help/topic/com.arm.doc.ddi0201d/DDI0201D_arm946es_r1p1_trm.pdf
- */
- asm volatile (
- /* Region 0 - addr=0, size=4GB, enabled */
- "mov r0, #0x3f \n\t"
- "mcr p15, 0, r0, c6, c0, 0 \n\t"
- "mcr p15, 0, r0, c6, c0, 1 \n\t"
-
-#if defined(LOGIK_DAX) || defined(SANSA_C100)
- /* Address region 1 - addr 0x2fff0000, size=64KB, enabled*/
- "ldr r0, =0x2fff001f \n\t"
-#elif defined(IAUDIO_7)
- /* Address region 1 - addr 0x20000000, size=8KB, enabled*/
- "mov r0, #0x19 \n\t"
- "add r0, r0, #0x20000000 \n\t"
-#elif defined(SANSA_M200)
- /* Address region 1 - addr 0x20000000, size=256MB, enabled*/
- "mov r0, #0x37 \n\t"
- "add r0, r0, #0x20000000 \n\t"
-#endif
- "mcr p15, 0, r0, c6, c1, 0 \n\t"
- "mcr p15, 0, r0, c6, c1, 1 \n\t"
-
- /* Address region 2 - addr 0x30000000, size=256MB, enabled*/
- "mov r0, #0x37 \n\t"
- "add r0, r0, #0x30000000 \n\t"
- "mcr p15, 0, r0, c6, c2, 0 \n\t"
- "mcr p15, 0, r0, c6, c2, 1 \n\t"
-
- /* Address region 2 - addr 0x40000000, size=512MB, enabled*/
- "mov r0, #0x39 \n\t"
- "add r0, r0, #0x40000000 \n\t"
- "mcr p15, 0, r0, c6, c3, 0 \n\t"
- "mcr p15, 0, r0, c6, c3, 1 \n\t"
-
- /* Address region 4 - addr 0x60000000, size=256MB, enabled*/
- "mov r0, #0x37 \n\t"
- "add r0, r0, #0x60000000 \n\t"
- "mcr p15, 0, r0, c6, c4, 0 \n\t"
- "mcr p15, 0, r0, c6, c4, 1 \n\t"
-
- /* Address region 5 - addr 0x10000000, size=256MB, enabled*/
- "mov r0, #0x37 \n\t"
- "add r0, r0, #0x10000000 \n\t"
- "mcr p15, 0, r0, c6, c5, 0 \n\t"
- "mcr p15, 0, r0, c6, c5, 1 \n\t"
-
- /* Address region 6 - addr 0x80000000, size=2GB, enabled*/
- "mov r0, #0x37 \n\t"
- "add r0, r0, #0x80000006 \n\t"
- "mcr p15, 0, r0, c6, c6, 0 \n\t"
- "mcr p15, 0, r0, c6, c6, 1 \n\t"
-
- /* Address region 7 - addr 0x3000f000, size=4KB, enabled*/
- "ldr r0, =0x3000f017 \n\t"
- "mcr p15, 0, r0, c6, c7, 0 \n\t"
- "mcr p15, 0, r0, c6, c7, 1 \n\t"
-
-
- /* Register 5 - Access Permission Registers */
-
- "ldr r0, =0xffff \n\t"
- "mcr p15, 0, r0, c5, c0, 0 \n\t" /* write data access permission bits */
- "mcr p15, 0, r0, c5, c0, 1 \n\t" /* write instruction access permission bits */
-
- "mov r0, #0xa7 \n\t"
- "mcr p15, 0, r0, c3, c0, 0 \n\t" /* set write buffer control register */
-
-#if defined(LOGIK_DAX) || defined(SANSA_C100)
- "mov r0, #0xa5 \n\t"
-#elif defined(IAUDIO_7) || defined(SANSA_M200)
- "mov r0, #0xa7 \n\t"
-#else
- #error NOT DEFINED FOR THIS TARGET!
-#endif
- "mcr p15, 0, r0, c2, c0, 0 \n\t"
- "mcr p15, 0, r0, c2, c0, 1 \n\t"
-
- "mov r0, #0xa0000006 \n\t"
- "mcr p15, 0, r0, c9, c1, 0 \n\t"
-
- "ldr r1, =0x1107d \n\t"
- "mov r0, #0x0 \n\t"
- "mcr p15, 0, r0, c7, c5, 0 \n\t" /* Flush instruction cache */
- "mcr p15, 0, r0, c7, c6, 0 \n\t" /* Flush data cache */
-
- "mcr p15, 0, r1, c1, c0, 0 \n\t" /* CPU control bits */
- : : : "r0", "r1"
- );
-}
-
-
-
-void system_init(void)
-{
- /* mask all interrupts */
- IEN = 0;
-
- /* Set all interrupts as IRQ for now - some may need to be FIQ in future */
- IRQSEL = 0xffffffff;
-
- /* Set master enable bit */
- IEN = 0x80000000;
-
- cpu_init();
- clock_init();
- gpio_init();
-
- enable_irq();
-}
-
-int system_memory_guard(int newmode)
-{
- (void)newmode;
- return 0;
-}
-
-#ifdef HAVE_ADJUSTABLE_CPU_FREQ
-
-void set_cpu_frequency(long frequency)
-{
-}
-
-#endif
diff --git a/firmware/target/arm/tcc77x/timer-tcc77x.c b/firmware/target/arm/tcc77x/timer-tcc77x.c
deleted file mode 100644
index 6e8764d9ce..0000000000
--- a/firmware/target/arm/tcc77x/timer-tcc77x.c
+++ /dev/null
@@ -1,69 +0,0 @@
-/***************************************************************************
-* __________ __ ___.
-* Open \______ \ ____ ____ | | _\_ |__ _______ ___
-* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
-* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
-* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
-* \/ \/ \/ \/ \/
-* $Id$
-*
-* Copyright (C) 2008 by Rob Purchase
-*
-* 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 "cpu.h"
-#include "system.h"
-#include "timer.h"
-#include "logf.h"
-
-/* Use the TC32 counter [sourced by Xin:12Mhz] for this timer, as it's the
- only one that allows a 32-bit counter (Timer0-5 are 16/20 bit only). */
-
-bool timer_set(long cycles, bool start)
-{
- #warning function not implemented
-
- (void)cycles;
- (void)start;
- return false;
-}
-
-bool timer_start(void)
-{
- #warning function not implemented
-
- return false;
-}
-
-void timer_stop(void)
-{
- #warning function not implemented
-}
-
-
-/* Timer interrupt processing - all timers (inc. tick) have a single IRQ */
-void TIMER(void)
-{
- if (TIREQ & TF0) /* Timer0 reached ref value */
- {
- /* Run through the list of tick tasks */
- call_tick_tasks();
-
- /* reset Timer 0 IRQ & ref flags */
- TIREQ |= TI0 | TF0;
- }
-
- if (TC32IRQ & (1<<3)) /* end of TC32 prescale */
- {
- /* dispatch timer */
- }
-}
diff --git a/firmware/target/arm/tms320dm320/app.lds b/firmware/target/arm/tms320dm320/app.lds
index 6ceb512d21..b35ace1ce6 100644
--- a/firmware/target/arm/tms320dm320/app.lds
+++ b/firmware/target/arm/tms320dm320/app.lds
@@ -14,22 +14,29 @@ STARTUP(target/arm/tms320dm320/crt0.o)
#define LCD_NATIVE_HEIGHT LCD_HEIGHT
#endif
-#define LCD_FUDGE LCD_NATIVE_WIDTH%32
-
-#define LCD_BUFFER_SIZE ((LCD_NATIVE_WIDTH+LCD_FUDGE)*LCD_NATIVE_HEIGHT*2)
-
/* must be 16Kb (0x4000) aligned */
#define TTB_SIZE 0x4000
+#define DRAMSIZE (MEMORYSIZE * 0x100000)
+#define DRAMORIG CONFIG_SDRAM_START
+
+
+#ifdef MROBE_500
+#define LCD_FUDGE LCD_NATIVE_WIDTH%32
+#define LCD_BUFFER_SIZE ((LCD_NATIVE_WIDTH+LCD_FUDGE)*LCD_NATIVE_HEIGHT*2)
/* Give this some memory to allow it to align to the MMU boundary.
* Note that since there are two buffers (YUV/RGB) it calculates the approximate
* memory needed in steps of 1 Meg.
*/
-#define LCD_TTB_AREA 0x100000*((LCD_BUFFER_SIZE>>19)+1)
-
-#define DRAMSIZE (MEMORYSIZE * 0x100000)
+#define LCD_TTB_AREA (0x100000*((LCD_BUFFER_SIZE>>19)+1))
+#else
+#define LCD_BUFFER_SIZE (LCD_NATIVE_WIDTH*LCD_NATIVE_HEIGHT*2)
+#define LCD_TTB_AREA (TTB_SIZE + LCD_BUFFER_SIZE)
+#endif
-#define DRAMORIG CONFIG_SDRAM_START
+/* End of the audio buffer, where the codec buffer starts */
+#define ENDAUDIOADDR \
+ (DRAMORIG + DRAMSIZE - PLUGIN_BUFFER_SIZE - CODEC_SIZE - LCD_TTB_AREA)
#define FLASHORIG 0x00100000
#define FLASHSIZE 0x00800000
@@ -44,10 +51,6 @@ PRO_STACK_SIZE = 0x2000;
IRQ_STACK_SIZE = 0x600;
FIQ_STACK_SIZE = 0x400;
-/* End of the audio buffer, where the codec buffer starts */
-#define ENDAUDIOADDR \
- (DRAMORIG + DRAMSIZE - PLUGIN_BUFFER_SIZE - CODEC_SIZE - LCD_TTB_AREA)
-
MEMORY
{
DRAM : ORIGIN = DRAMORIG, LENGTH = DRAMSIZE
@@ -105,7 +108,7 @@ SECTIONS
*(.rodata*)
} > DRAM
- .data :
+ .data :
{
. = ALIGN(0x4);
*(.data*)
@@ -142,12 +145,12 @@ SECTIONS
} > ITCM
/* Program stack space */
- .pro_stack (NOLOAD):
+ .pro_stack (NOLOAD):
{
. = ALIGN(0x4);
*(.stack)
stackbegin = .; /* Variable for thread.c */
- _pro_stack_end = .;
+ _pro_stack_end = .;
. += PRO_STACK_SIZE;
_pro_stack_start = .;
stackend = .; /* Variable for tread.c */
@@ -193,31 +196,48 @@ SECTIONS
pluginbuf = .;
. += PLUGIN_BUFFER_SIZE;
} > DRAM
-
+
_endsdram = .;
+#ifdef MROBE_500
.ttbtable (NOLOAD) :
{
. = ALIGN (0x4000);
_ttbstart = .;
. += TTB_SIZE;
} > DRAM
-
+
/* The LCD buffer should be at the end of memory to protect against
- * overflowing something else when the YUV blitter is fudging the screen
+ * overflowing something else when the YUV blitter is fudging the screen
* size.
*/
-
+
.lcdbuffer (NOLOAD) :
{
_lcdbuf = .;
. += LCD_BUFFER_SIZE;
} > DRAM
-
+
.lcdbuffer2 (NOLOAD) :
{
_lcdbuf2 = .;
. += LCD_BUFFER_SIZE;
} > DRAM
+#else
+ .lcdbuffer (NOLOAD) :
+ {
+ . = ALIGN(32);
+ _lcdbuf = .;
+ . += LCD_BUFFER_SIZE;
+ } > DRAM
+
+ /* Place TTB at the end of RAM to minimize alignment losses */
+ .ttbtable (NOLOAD) :
+ {
+ . = ALIGN (0x4000);
+ _ttbstart = .;
+ . += TTB_SIZE;
+ } > DRAM
+#endif
}
diff --git a/firmware/target/arm/tms320dm320/boot.lds b/firmware/target/arm/tms320dm320/boot.lds
index c59cc7f514..8b075f43df 100644
--- a/firmware/target/arm/tms320dm320/boot.lds
+++ b/firmware/target/arm/tms320dm320/boot.lds
@@ -14,22 +14,28 @@ STARTUP(target/arm/tms320dm320/crt0.o)
#define LCD_NATIVE_HEIGHT LCD_HEIGHT
#endif
-#define LCD_FUDGE LCD_NATIVE_WIDTH%32
-
-#define LCD_BUFFER_SIZE ((LCD_NATIVE_WIDTH+LCD_FUDGE)*LCD_NATIVE_HEIGHT*2)
-
/* must be 16Kb (0x4000) aligned */
#define TTB_SIZE (0x4000)
+/* Bootloader only uses/knows about the upper 32 M */
+#define DRAMSIZE (MEMORYSIZE * 0x100000 / 2)
+#define DRAMORIG CONFIG_SDRAM_START+DRAMSIZE
+
+#ifdef MROBE_500
+#define LCD_FUDGE LCD_NATIVE_WIDTH%32
+#define LCD_BUFFER_SIZE ((LCD_NATIVE_WIDTH+LCD_FUDGE)*LCD_NATIVE_HEIGHT*2)
/* Give this some memory to allow it to align to the MMU boundary.
* Note that since there are two buffers (YUV/RGB) it calculates the approximate
* memory needed in steps of 1 Meg.
*/
-#define LCD_TTB_AREA 0x100000*((LCD_BUFFER_SIZE>>19)+1)
-
-/* Bootloader only uses/knows about the upper 32 M */
-#define DRAMORIG CONFIG_SDRAM_START+0x02000000
-#define DRAMSIZE (MEMORYSIZE * 0x80000)
+#define LCD_TTB_AREA (0x100000*((LCD_BUFFER_SIZE>>19)+1))
+/* End of the audio buffer, where the codec buffer starts */
+#define TTB_BEGIN (DRAMORIG + DRAMSIZE - LCD_TTB_AREA)
+#else
+#define LCD_BUFFER_SIZE (LCD_NATIVE_WIDTH*LCD_NATIVE_HEIGHT*2)
+#define LCD_TTB_AREA (TTB_SIZE + LCD_BUFFER_SIZE)
+#define LCD_BEGIN (DRAMORIG + DRAMSIZE - LCD_TTB_AREA)
+#endif
#define IRAMORIG 0x00000000
#define IRAMSIZE 0x4000
@@ -38,12 +44,12 @@ STARTUP(target/arm/tms320dm320/crt0.o)
#ifdef SANSA_CONNECT
/* Offset in flash from beginning, we don't want overwrite OF bootloader
- due to recovery mode and more importantly - hardware block protection.
- This offset makes Rockbox bootloader a replacement for OF vmlinux.
- In .srr file header add any valid memory address from following
- <0x1000000; 0x1300180) u (0x131EAF4; 0x1420000) u (0x1440000; 0x5000000>
- ensuring that complete bootloader fits in.
- Entry point in .srr file should be equal to _loadaddress. */
+ * due to recovery mode and more importantly - hardware block protection.
+ * Rockbox bootloader is flashed into kernel partition and chainloaded
+ * from OF bootloader via Arbitrary Code Execution exploit. The first
+ * instruction must be position independent as Rockbox bootloader will be
+ * copied to RAM at 0x01000000 and executed from RAM.
+ */
#define FLASHSIZE 0x00400000
#define FLASHMEMORIG 0x00120010
/* Kernel partition is 2 M, srr header is 16 bytes, sig is 2048 bytes */
@@ -59,9 +65,6 @@ PRO_STACK_SIZE = 0x2000;
IRQ_STACK_SIZE = 0x400;
FIQ_STACK_SIZE = 0x400;
-/* End of the audio buffer, where the codec buffer starts */
-#define TTB_BEGIN (DRAMORIG + DRAMSIZE - LCD_TTB_AREA)
-
MEMORY
{
DRAM : ORIGIN = DRAMORIG, LENGTH = DRAMSIZE
@@ -148,7 +151,7 @@ SECTIONS
} > IRAM AT> FLASH
_iramcopy = LOADADDR(.iram);
-
+
.ibss (NOLOAD) :
{
. = ALIGN(0x4);
@@ -158,12 +161,12 @@ SECTIONS
} > IRAM
/* Program stack space */
- .pro_stack (NOLOAD):
+ .pro_stack (NOLOAD):
{
. = ALIGN(0x4);
*(.stack)
stackbegin = .; /* Variable for thread.c */
- _pro_stack_end = .;
+ _pro_stack_end = .;
. += PRO_STACK_SIZE;
_pro_stack_start = .;
stackend = .; /* Variable for tread.c */
@@ -186,28 +189,45 @@ SECTIONS
. += FIQ_STACK_SIZE;
_fiq_stack_start = .;
} > IRAM
-
+
+#ifdef MROBE_500
.ttbtable TTB_BEGIN (NOLOAD) :
{
. = ALIGN (0x4000);
_ttbstart = .;
. += TTB_SIZE;
} > DRAM
-
+
/* The LCD buffer should be at the end of memory to protect against
- * overflowing something else when the YUV blitter is fudging the screen
+ * overflowing something else when the YUV blitter is fudging the screen
* size.
*/
-
+
.lcdbuffer (NOLOAD) :
{
_lcdbuf = .;
. += LCD_BUFFER_SIZE;
} > DRAM
-
+
.lcdbuffer2 (NOLOAD) :
{
_lcdbuf2 = .;
. += LCD_BUFFER_SIZE;
} > DRAM
+#else
+ .lcdbuffer LCD_BEGIN (NOLOAD) :
+ {
+ . = ALIGN(32);
+ _lcdbuf = .;
+ . += LCD_BUFFER_SIZE;
+ } > DRAM
+
+ /* Place TTB at the end of RAM to minimize alignment losses */
+ .ttbtable (NOLOAD) :
+ {
+ . = ALIGN (0x4000);
+ _ttbstart = .;
+ . += TTB_SIZE;
+ } > DRAM
+#endif
}
diff --git a/firmware/target/arm/tms320dm320/creative-zvm/ata-creativezvm.c b/firmware/target/arm/tms320dm320/creative-zvm/ata-creativezvm.c
index f13904628d..a1985472a0 100644
--- a/firmware/target/arm/tms320dm320/creative-zvm/ata-creativezvm.c
+++ b/firmware/target/arm/tms320dm320/creative-zvm/ata-creativezvm.c
@@ -303,7 +303,7 @@ static void cfs_init(void)
_ata_read_sectors(CFS_CLUSTER2CLUSTER(vfat_inodes_nr[1]), 1, &sector);
inode = (struct cfs_inode*)&sector;
#ifndef BOOTLOADER
- sectors_handle = core_alloc("ata sectors", VFAT_SECTOR_SIZE(inode->filesize));
+ sectors_handle = core_alloc(VFAT_SECTOR_SIZE(inode->filesize));
unsigned long *sectors = core_get_data(sectors_handle);
#else
static unsigned long _sector[VFAT_SECTOR_SIZE(1024*1024*1024)]; /* 1GB guess */
diff --git a/firmware/target/arm/tms320dm320/creative-zvm/pcm-creativezvm.c b/firmware/target/arm/tms320dm320/creative-zvm/pcm-creativezvm.c
index d5e30ad73b..fa1eb2481f 100644
--- a/firmware/target/arm/tms320dm320/creative-zvm/pcm-creativezvm.c
+++ b/firmware/target/arm/tms320dm320/creative-zvm/pcm-creativezvm.c
@@ -52,12 +52,6 @@ void pcm_play_dma_postinit(void)
// dsp_wake();
}
-const void * pcm_play_dma_get_peak_buffer(int *count)
-{
- (void) count;
- return 0;
-}
-
void pcm_dma_apply_settings(void)
{
audiohw_set_frequency(pcm_fsel);
diff --git a/firmware/target/arm/tms320dm320/crt0.S b/firmware/target/arm/tms320dm320/crt0.S
index 9f2c8dbe04..e57e28a470 100644
--- a/firmware/target/arm/tms320dm320/crt0.S
+++ b/firmware/target/arm/tms320dm320/crt0.S
@@ -201,14 +201,14 @@ _start:
mov r3, #CACHE_NONE
bl map_section
- /* Enable caching for FLASH */
+ /* Enable write-through caching for FLASH */
ldr r0, =_flash_start
ldr r1, =_flash_start
ldr r2, =_flash_sizem
- mov r3, #CACHE_ALL
+ mov r3, #(CACHE_ALL & ~BUFFERED)
bl map_section
- /* Enable caching for RAM */
+ /* Enable write-back caching for RAM */
ldr r0, =_sdram_start
ldr r1, =_sdram_start
ldr r2, =_sdram_sizem
diff --git a/firmware/target/arm/tms320dm320/i2c-dm320.c b/firmware/target/arm/tms320dm320/i2c-dm320.c
index 629dae394c..364d3b5c17 100644
--- a/firmware/target/arm/tms320dm320/i2c-dm320.c
+++ b/firmware/target/arm/tms320dm320/i2c-dm320.c
@@ -294,4 +294,12 @@ int i2c_read_bytes(unsigned short address, unsigned short reg,
return i2c_read_data(dm320_i2c_bus, address, reg, buf, count);
}
+int i2c_write_read_bytes(unsigned short address,
+ const unsigned char* buf_write, int count_write,
+ unsigned char* buf_read, int count_read)
+{
+ return i2c_write_read_data(dm320_i2c_bus, address, buf_write, count_write,
+ buf_read, count_read);
+}
+
#endif
diff --git a/firmware/target/arm/tms320dm320/i2c-dm320.h b/firmware/target/arm/tms320dm320/i2c-dm320.h
index 7dfc19f046..89a8e6f1a4 100644
--- a/firmware/target/arm/tms320dm320/i2c-dm320.h
+++ b/firmware/target/arm/tms320dm320/i2c-dm320.h
@@ -24,13 +24,16 @@
#include "system.h"
-void i2c_init(void);
+void i2c_init(void) INIT_ATTR;
int i2c_write(unsigned short address, const unsigned char *data, int count);
int i2c_read(unsigned short address, unsigned char* buf, int count);
#ifdef HAVE_SOFTWARE_I2C
int i2c_read_bytes(unsigned short address, unsigned short reg,
unsigned char* buf, int count);
+int i2c_write_read_bytes(unsigned short address,
+ const unsigned char* buf_write, int count_write,
+ unsigned char* buf_read, int count_read);
#endif
#endif
diff --git a/firmware/target/arm/tms320dm320/mrobe-500/lcd-mr500.c b/firmware/target/arm/tms320dm320/mrobe-500/lcd-mr500.c
index d952d3d40d..8620c672e1 100644
--- a/firmware/target/arm/tms320dm320/mrobe-500/lcd-mr500.c
+++ b/firmware/target/arm/tms320dm320/mrobe-500/lcd-mr500.c
@@ -38,7 +38,7 @@
#if CONFIG_ORIENTATION == SCREEN_PORTRAIT
#define LCD_USE_DMA
-#elif defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#elif LCD_STRIDEFORMAT == VERTICAL_STRIDE
#define LCD_USE_DMA
#endif
@@ -511,7 +511,7 @@ void lcd_update_rect(int x, int y, int width, int height)
#else
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
#if defined(LCD_USE_DMA)
dma_start_transfer16( (char *)FBADDR(0,0), x, y, LCD_HEIGHT,
diff --git a/firmware/target/arm/tms320dm320/mrobe-500/pcm-mr500.c b/firmware/target/arm/tms320dm320/mrobe-500/pcm-mr500.c
index 5ce5a8f399..fda8615e5a 100644
--- a/firmware/target/arm/tms320dm320/mrobe-500/pcm-mr500.c
+++ b/firmware/target/arm/tms320dm320/mrobe-500/pcm-mr500.c
@@ -46,22 +46,6 @@ void pcm_play_dma_postinit(void)
audiohw_postinit();
}
-/* Return the current location in the SDRAM to SARAM transfer along with the
- * number of bytes read in the current buffer (count). There is latency with
- * this method equivalent to ~ the size of the SARAM buffer since there is
- * another buffer between your ears and this calculation, but this works for
- * key clicks and an approximate peak meter.
- */
-const void * pcm_play_dma_get_peak_buffer(int *count)
-{
- int cnt = DSP_(_sdem_level);
-
- unsigned long addr = (unsigned long) start + cnt;
-
- *count = (cnt & 0xFFFFF) >> 1;
- return (void *)((addr + 2) & ~3);
-}
-
void pcm_play_dma_init(void)
{
IO_INTC_IRQ0 = INTR_IRQ0_IMGBUF;
diff --git a/firmware/target/arm/tms320dm320/sansa-connect/avr-sansaconnect.c b/firmware/target/arm/tms320dm320/sansa-connect/avr-sansaconnect.c
index 611bdbfde5..6fd504a0f8 100644
--- a/firmware/target/arm/tms320dm320/sansa-connect/avr-sansaconnect.c
+++ b/firmware/target/arm/tms320dm320/sansa-connect/avr-sansaconnect.c
@@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id: $
*
-* Copyright (C) 2011 by Tomasz Moń
+* Copyright (C) 2011-2021 by Tomasz Moń
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -21,11 +21,17 @@
#include <stdio.h>
#include "config.h"
+#include "file.h"
#include "system.h"
+#include "time.h"
+#include "power.h"
#include "kernel.h"
+#include "panic.h"
+/*#define LOGF_ENABLE*/
#include "logf.h"
#include "avr-sansaconnect.h"
#include "uart-target.h"
+#include "usb.h"
#include "button.h"
#include "backlight.h"
#include "powermgmt.h"
@@ -46,53 +52,119 @@
#define dbgprintf(...)
#endif
-#define CMD_SYNC 0xAA
-#define CMD_CLOSE 0xCC
-#define CMD_LCM_POWER 0xC9
-#define LCM_POWER_OFF 0x00
-#define LCM_POWER_ON 0x01
-#define LCM_POWER_SLEEP 0x02
-#define LCM_POWER_WAKE 0x03
-#define LCM_REPOWER_ON 0x04
-
-#define CMD_STATE 0xBB
-#define CMD_VER 0xBC
-#define CMD_WHEEL_EN 0xD0
-#define CMD_SET_INTCHRG 0xD1
-#define CMD_CODEC_RESET 0xD7
-#define CMD_FILL 0xFF
-
-#define CMD_SYS_CTRL 0xDA
-#define SYS_CTRL_POWEROFF 0x00
+#define AVR_DELAY_US 200
+#define AVR_MAX_RETRIES 10
+
+#define CMD_SYNC 0xAA
+#define CMD_CLOSE 0xCC
+#define CMD_FILL 0xFF
+
+/* Actual command opcodes handled by AVR */
+#define CMD_STATE 0xBB
+#define CMD_VER 0xBC
+#define CMD_MONOTIME 0xBD
+#define CMD_PGMWAKE 0xBF
+#define CMD_HDQ_READ 0xC0
+#define CMD_HDQ_WRITE 0xC1
+#define CMD_HDQ_STATUS 0xC2
+#define CMD_GET_LAST_RESET_TYPE 0xC4
+#define CMD_UNKNOWN_C5 0xC5
+#define CMD_GET_BATTERY_TEMP 0xC8
+#define CMD_LCM_POWER 0xC9
+#define CMD_AMP_ENABLE 0xCA
+#define CMD_WHEEL_EN 0xD0
+#define CMD_SET_INTCHRG 0xD1
+#define CMD_GET_INTCHRG 0xD2
+#define CMD_WIFI_PD 0xD3
+#define CMD_UNKNOWN_D4 0xD4
+#define CMD_UNKNOWN_D5 0xD5
+#define CMD_UNKNOWN_D6 0xD6
+#define CMD_CODEC_RESET 0xD7
+#define CMD_ADC_START 0xD8
+#define CMD_ADC_RESULT 0xD9
+#define CMD_SYS_CTRL 0xDA
+#define CMD_SET_USBCHRG 0xE2
+#define CMD_GET_USBCHRG 0xE3
+#define CMD_MONORSTCNT 0xE4
+
+/* CMD_LCM_POWER parameters */
+#define LCM_POWER_OFF 0x00
+#define LCM_POWER_ON 0x01
+#define LCM_POWER_SLEEP 0x02
+#define LCM_POWER_WAKE 0x03
+#define LCM_REPOWER_ON 0x04
+
+/* CMD_SYS_CTRL parameters */
+#define SYS_CTRL_POWEROFF 0x00
+#define SYS_CTRL_RESET 0x01
+#define SYS_CTRL_SLEEP 0x02
+#define SYS_CTRL_DISABLE_WD 0x03
+#define SYS_CTRL_KICK_WD 0x04
+#define SYS_CTRL_EN_HDQ_THERM 0x05
+#define SYS_CTRL_EN_TS_THERM 0x06
+#define SYS_CTRL_FRESET 0x80
+
+/* HDQ status codes */
+#define HDQ_STATUS_OK 0x00
+#define HDQ_STATUS_NOT_READY 0x01
+#define HDQ_STATUS_TIMEOUT 0x02
/* protects spi avr commands from concurrent access */
static struct mutex avr_mtx;
+/* serializes hdq read/write and status retrieval */
+static struct mutex hdq_mtx;
-/* buttons thread */
-#define BTN_INTERRUPT 1
+/* AVR thread events */
+#define INPUT_INTERRUPT 1
+#define MONOTIME_OFFSET_UPDATE 2
+#define POWER_OFF_REQUEST 3
static int btn = 0;
static bool hold_switch;
-#ifndef BOOTLOADER
-static long btn_stack[DEFAULT_STACK_SIZE/sizeof(long)];
-static const char btn_thread_name[] = "buttons";
-static struct event_queue btn_queue;
-#endif
-
-static inline unsigned short be2short(unsigned char* buf)
+static bool input_interrupt_pending;
+/* AVR implements 32-bit counter incremented every second.
+ * The counter value cannot be modified to arbitrary value,
+ * so the epoch offset needs to be stored in a file.
+ */
+#define MONOTIME_OFFSET_FILE ROCKBOX_DIR "/monotime_offset.dat"
+static uint32_t monotime_offset;
+/* Buffer last read monotime value. Reading monotime takes
+ * atleast 1400 us so the tick counter is used together with
+ * last read monotime value to return current time.
+ */
+static bool monotime_available;
+static uint32_t monotime_value;
+static unsigned long monotime_value_tick;
+
+static long avr_stack[DEFAULT_STACK_SIZE/sizeof(long)];
+static const char avr_thread_name[] = "avr";
+static struct event_queue avr_queue;
+
+/* OF bootloader will refuse to start software if low power is set
+ * Bits 3, 4, 5, 6 and 7 are unknown.
+ */
+#define BATTERY_STATUS_LOW_POWER (1 << 2)
+#define BATTERY_STATUS_CHARGER_CONNECTED (1 << 1)
+#define BATTERY_STATUS_CHARGING (1 << 0)
+static uint8_t avr_battery_status;
+
+#define BATTERY_LEVEL_NOT_DETECTED (1 << 7)
+#define BATTERY_LEVEL_PERCENTAGE_MASK 0x7F
+static uint8_t avr_battery_level = 100;
+
+static inline uint16_t be2short(uint8_t *buf)
{
- return (unsigned short)((buf[0] << 8) | buf[1]);
+ return (uint16_t)((buf[0] << 8) | buf[1]);
}
#define BUTTON_DIRECT_MASK (BUTTON_LEFT | BUTTON_UP | BUTTON_RIGHT | BUTTON_DOWN | BUTTON_SELECT | BUTTON_VOL_UP | BUTTON_VOL_DOWN | BUTTON_NEXT | BUTTON_PREV)
-#ifndef BOOTLOADER
-static void handle_wheel(unsigned char wheel)
+static void handle_wheel(uint8_t wheel)
{
static int key = 0;
- static unsigned char velocity = 0;
- static unsigned long wheel_delta = 1ul << 24;
- static unsigned char wheel_prev = 0;
- static long nextbacklight_hw_on = 0;
+ static uint8_t velocity = 0;
+ static uint32_t wheel_delta = 1ul << 24;
+ static uint8_t wheel_prev = 0;
+ static unsigned long nextbacklight_hw_on = 0;
static int prev_key = -1;
static int prev_key_post = 0;
@@ -151,49 +223,45 @@ static void handle_wheel(unsigned char wheel)
prev_key = key;
}
-#endif
-/* buf must be 11-byte array of byte (reply from avr_hid_get_state() */
-static void parse_button_state(unsigned char *buf)
+/* buf must be 8-byte state array (reply from avr_hid_get_state() */
+static void parse_button_state(uint8_t *state)
{
- unsigned short main_btns_state = be2short(&buf[4]);
+ uint16_t main_btns_state = be2short(&state[2]);
#ifdef BUTTON_DEBUG
- unsigned short main_btns_changed = be2short(&buf[6]);
+ uint16_t main_btns_changed = be2short(&state[4]);
#endif
/* make sure other bits doesn't conflict with our "free bits" buttons */
main_btns_state &= BUTTON_DIRECT_MASK;
- if (buf[3] & 0x01) /* is power button pressed? */
+ if (state[1] & 0x01) /* is power button pressed? */
{
main_btns_state |= BUTTON_POWER;
}
btn = main_btns_state;
-#ifndef BOOTLOADER
/* check if stored hold_switch state changed (prevents lost changes) */
- if ((buf[3] & 0x20) /* hold change notification */ ||
- (hold_switch != ((buf[3] & 0x02) >> 1)))
+ if ((state[1] & 0x20) /* hold change notification */ ||
+ (hold_switch != ((state[1] & 0x02) >> 1)))
{
-#endif
- hold_switch = (buf[3] & 0x02) >> 1;
+ hold_switch = (state[1] & 0x02) >> 1;
#ifdef BUTTON_DEBUG
dbgprintf("HOLD changed (%d)", hold_switch);
#endif
#ifndef BOOTLOADER
backlight_hold_changed(hold_switch);
- }
#endif
-#ifndef BOOTLOADER
- if ((hold_switch == false) && (buf[3] & 0x80)) /* scrollwheel change */
+ }
+
+ if ((hold_switch == false) && (state[1] & 0x80)) /* scrollwheel change */
{
- handle_wheel(buf[2]);
+ handle_wheel(state[0]);
}
-#endif
#ifdef BUTTON_DEBUG
- if (buf[3] & 0x10) /* power button change */
+ if (state[1] & 0x10) /* power button change */
{
/* power button state has changed */
main_btns_changed |= BUTTON_POWER;
@@ -215,182 +283,604 @@ static void parse_button_state(unsigned char *buf)
#endif
}
-static void spi_txrx(unsigned char *buf_tx, unsigned char *buf_rx, int n)
+static bool avr_command_reads_data(uint8_t opcode)
+{
+ switch (opcode)
+ {
+ case CMD_STATE:
+ case CMD_VER:
+ case CMD_GET_LAST_RESET_TYPE:
+ case CMD_GET_INTCHRG:
+ case CMD_MONOTIME:
+ case CMD_UNKNOWN_C5:
+ case CMD_MONORSTCNT:
+ case CMD_HDQ_STATUS:
+ case CMD_GET_BATTERY_TEMP:
+ case CMD_GET_USBCHRG:
+ case CMD_ADC_RESULT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static size_t avr_command_data_size(uint8_t opcode)
+{
+ switch (opcode)
+ {
+ case CMD_STATE: return 8;
+ case CMD_VER: return 1;
+ case CMD_MONOTIME: return 4;
+ case CMD_PGMWAKE: return 4;
+ case CMD_HDQ_READ: return 1;
+ case CMD_HDQ_WRITE: return 2;
+ case CMD_HDQ_STATUS: return 2;
+ case CMD_GET_LAST_RESET_TYPE: return 1;
+ case CMD_UNKNOWN_C5: return 1;
+ case CMD_GET_BATTERY_TEMP: return 2;
+ case CMD_LCM_POWER: return 1;
+ case CMD_AMP_ENABLE: return 1;
+ case CMD_WHEEL_EN: return 1;
+ case CMD_SET_INTCHRG: return 1;
+ case CMD_GET_INTCHRG: return 1;
+ case CMD_WIFI_PD: return 1;
+ case CMD_UNKNOWN_D4: return 1;
+ case CMD_UNKNOWN_D5: return 2;
+ case CMD_UNKNOWN_D6: return 2;
+ case CMD_CODEC_RESET: return 0;
+ case CMD_ADC_START: return 1;
+ case CMD_ADC_RESULT: return 2;
+ case CMD_SYS_CTRL: return 1;
+ case CMD_SET_USBCHRG: return 1;
+ case CMD_GET_USBCHRG: return 1;
+ case CMD_MONORSTCNT: return 2;
+ default:
+ panicf("Invalid AVR opcode %02X", opcode);
+ return 0;
+ }
+}
+
+static uint8_t spi_read_byte(void)
+{
+ uint16_t rxdata;
+
+ do
+ {
+ rxdata = IO_SERIAL1_RX_DATA;
+ }
+ while (rxdata & (1<<8));
+
+ return rxdata & 0xFF;
+}
+
+static void avr_hid_select(void)
+{
+ /* Drive GIO29 (AVR SS) low */
+ IO_GIO_BITCLR1 = (1 << 13);
+}
+
+static void avr_hid_release(void)
+{
+ /* Drive GIO29 (AVR SS) high */
+ IO_GIO_BITSET1 = (1 << 13);
+}
+
+static bool avr_run_command(uint8_t opcode, uint8_t *data, size_t data_length)
{
- int i;
- unsigned short rxdata;
+ bool success = true;
+ const bool is_read = avr_command_reads_data(opcode);
+ size_t i;
+ uint8_t rx;
+
+ /* Verify command data size and also make sure command is valid */
+ if (avr_command_data_size(opcode) != data_length)
+ {
+ panicf("AVR %02x invalid data length", opcode);
+ }
mutex_lock(&avr_mtx);
bitset16(&IO_CLK_MOD2, CLK_MOD2_SIF1);
IO_SERIAL1_TX_ENABLE = 0x0001;
- for (i = 0; i<n; i++)
- {
- IO_SERIAL1_TX_DATA = buf_tx[i];
+ avr_hid_select();
+ udelay(10);
- /* 100 us wait for AVR */
- udelay(100);
+ IO_SERIAL1_TX_DATA = CMD_SYNC;
+ spi_read_byte();
+ /* Allow AVR to process CMD_SYNC */
+ udelay(AVR_DELAY_US);
- do
- {
- rxdata = IO_SERIAL1_RX_DATA;
- } while (rxdata & (1<<8));
+ IO_SERIAL1_TX_DATA = opcode;
+ rx = spi_read_byte();
+ if (rx != CMD_SYNC)
+ {
+ /* AVR failed to register CMD_SYNC */
+ success = false;
+ }
+ /* Allow AVR to process opcode */
+ udelay(AVR_DELAY_US);
- if (buf_rx != NULL)
- buf_rx[i] = rxdata & 0xFF;
+ if (is_read)
+ {
+ for (i = 0; i < data_length; i++)
+ {
+ IO_SERIAL1_TX_DATA = CMD_FILL;
+ data[i] = spi_read_byte();
+ udelay(AVR_DELAY_US);
+ }
+ }
+ else
+ {
+ for (i = 0; i < data_length; i++)
+ {
+ IO_SERIAL1_TX_DATA = data[i];
+ spi_read_byte();
+ udelay(AVR_DELAY_US);
+ }
+ }
- /* 100 us wait to give AVR time to process data */
- udelay(100);
+ IO_SERIAL1_TX_DATA = CMD_CLOSE;
+ rx = spi_read_byte();
+ udelay(AVR_DELAY_US);
+ if (is_read)
+ {
+ success = success && (rx == CMD_CLOSE);
}
+ avr_hid_release();
+
IO_SERIAL1_TX_ENABLE = 0;
bitclr16(&IO_CLK_MOD2, CLK_MOD2_SIF1);
mutex_unlock(&avr_mtx);
+
+ return success;
}
-void avr_hid_sync(void)
+static bool avr_hid_get_state(void)
{
- int i;
- unsigned char prg[4] = {CMD_SYNC, CMD_VER, CMD_FILL, CMD_CLOSE};
+ uint8_t state[8];
+ if (avr_run_command(CMD_STATE, state, sizeof(state)))
+ {
+ avr_battery_status = state[6];
+ avr_battery_level = state[7];
+ parse_button_state(state);
+ return true;
+ }
+ return false;
+}
- /* Send SYNC three times */
- for (i = 0; i<3; i++)
+static bool avr_hid_sync(void)
+{
+ int retry;
+ for (retry = 0; retry < AVR_MAX_RETRIES; retry++)
{
- spi_txrx(prg, NULL, sizeof(prg));
+ if (avr_hid_get_state())
+ {
+ return true;
+ }
+ mdelay(100);
}
+ /* TODO: Program HID as it appears to be not programmed.
+ * To do so, unfortunately, AVR firmware would have to be written
+ * from scratch as OF blob cannot be used due to licensing.
+ */
+ return false;
+}
+
+static bool avr_execute_command(uint8_t opcode, uint8_t *data, size_t data_length)
+{
+ int retry;
+ for (retry = 0; retry < AVR_MAX_RETRIES; retry++)
+ {
+ if (avr_run_command(opcode, data, data_length))
+ {
+ return true;
+ }
+
+ /* Resync and try again */
+ avr_hid_sync();
+ }
+ return false;
}
void avr_hid_init(void)
{
/*
setup alternate GIO functions:
- GIO29 - SIF1 Enable (Directly connected to AVR's SS)
GIO30 - SIF1 Clock
GIO31 - SIF1 Data In
GIO32 - SIF1 Data Out
+ Manually drive GIO29 output (directly connected to AVR's SS).
*/
- IO_GIO_FSEL2 = (IO_GIO_FSEL2 & 0x00FF) | 0xAA00;
+ IO_GIO_FSEL2 = (IO_GIO_FSEL2 & 0x00FF) | 0xA800;
/* GIO29, GIO30 - outputs, GIO31 - input */
IO_GIO_DIR1 = (IO_GIO_DIR1 & ~((1 << 13) | (1 << 14))) | (1 << 15);
/* GIO32 - output */
bitclr16(&IO_GIO_DIR2, (1 << 0));
+ avr_hid_release();
- /* RATE = 219 (0xDB) -> 200 kHz */
+ /* Master, MSB first, RATE = 219 (Bit rate = ARM clock / 2*(RATE + 1)))
+ * Boosted 148.5 MHz / 440 = 337.5 kHz
+ * Default 74.25 MHz / 440 = 168.75 kHz
+ */
IO_SERIAL1_MODE = 0x6DB;
mutex_init(&avr_mtx);
+ mutex_init(&hdq_mtx);
+}
+
+int _battery_level(void)
+{
+ /* OF still plays music when level is at 0 */
+ if (avr_battery_level & BATTERY_LEVEL_NOT_DETECTED)
+ {
+ return 0;
+ }
+ return avr_battery_level & BATTERY_LEVEL_PERCENTAGE_MASK;
}
-/* defined in powermgmt-sansaconnect.c */
-void set_battery_level(unsigned int level);
+int _battery_voltage(void)
+{
+ return avr_hid_hdq_read_short(HDQ_REG_VOLT);
+}
-static void avr_hid_get_state(void)
+int _battery_time(void)
{
- static unsigned char cmd[11] = {CMD_SYNC, CMD_STATE,
- CMD_FILL, CMD_FILL, CMD_FILL, CMD_FILL, CMD_FILL, CMD_FILL, CMD_FILL, CMD_FILL,
- CMD_CLOSE};
+ /* HDQ_REG_TTE reads as 65535 when charging */
+ return avr_hid_hdq_read_short(HDQ_REG_TTE);
+}
- static unsigned char buf[11];
- static unsigned char cmd_empty[1] = {0xCC};
+unsigned int power_input_status(void)
+{
+ if (avr_battery_status & BATTERY_STATUS_CHARGER_CONNECTED)
+ {
+ return POWER_INPUT_USB_CHARGER;
+ }
+ return POWER_INPUT_NONE;
+}
- spi_txrx(cmd, buf, sizeof(cmd));
+bool charging_state(void)
+{
+ return (avr_battery_status & BATTERY_STATUS_CHARGING) != 0;
+}
- /*
- * buf[8] contains some battery/charger related information (unknown)
- * buf[9] contains battery level in percents (0-100)
- */
- set_battery_level((unsigned int)buf[9]);
+static int avr_hid_hdq_read_byte_internal(uint8_t address)
+{
+ uint8_t result[2];
- spi_txrx(cmd_empty, NULL, 1); /* request interrupt on button press */
+ if (!avr_execute_command(CMD_HDQ_READ, &address, sizeof(address)))
+ {
+ return -1;
+ }
- parse_button_state(buf);
+ do
+ {
+ mdelay(10);
+ if (!avr_execute_command(CMD_HDQ_STATUS, result, sizeof(result)))
+ {
+ return -1;
+ }
+ }
+ while (result[0] == HDQ_STATUS_NOT_READY);
+
+ if (result[0] != HDQ_STATUS_OK)
+ {
+ logf("HDQ read %d status %d", address, result[0]);
+ return -1;
+ }
+
+ return result[1];
}
-static void avr_hid_enable_wheel(void)
+int avr_hid_hdq_read_byte(uint8_t address)
{
- unsigned char wheel_en[4] = {CMD_SYNC, CMD_WHEEL_EN, 0x01, CMD_CLOSE};
+ int retry;
+ int value = -1;
+ for (retry = 0; (retry < 3) && (value < 0); retry++)
+ {
+ mutex_lock(&hdq_mtx);
+ value = avr_hid_hdq_read_byte_internal(address);
+ mutex_unlock(&hdq_mtx);
+ }
+ return value;
+}
- spi_txrx(wheel_en, NULL, sizeof(wheel_en));
+int avr_hid_hdq_read_short(uint8_t address)
+{
+ int old_hi = -1, old_lo = -1, hi = -2, lo = -2;
+ /* Keep reading until we read the same value twice.
+ * There's no atomic 16-bit value retrieval, so keep reading
+ * until we read the same value twice. HDQ registers update
+ * no more than once per 2.56 seconds so usually there will
+ * be 4 reads and sometimes 6 reads.
+ */
+ while ((old_hi != hi) || (old_lo != lo))
+ {
+ old_hi = hi;
+ old_lo = lo;
+ hi = avr_hid_hdq_read_byte(address + 1);
+ lo = avr_hid_hdq_read_byte(address);
+ }
+ if ((hi < 0) || (lo < 0))
+ {
+ return -1;
+ }
+ return (hi << 8) | lo;
+}
+
+static void avr_hid_enable_wheel(void)
+{
+ uint8_t enable = 0x01;
+ avr_execute_command(CMD_WHEEL_EN, &enable, sizeof(enable));
}
/* command that is sent by "hidtool -J 1" issued on every OF boot */
void avr_hid_enable_charger(void)
{
- unsigned char charger_en[4] = {CMD_SYNC, CMD_SET_INTCHRG, 0x01, CMD_CLOSE};
-
- spi_txrx(charger_en, NULL, sizeof(charger_en));
+ uint8_t enable = 0x01;
+ avr_execute_command(CMD_SET_INTCHRG, &enable, sizeof(enable));
}
-void avr_hid_lcm_sleep(void)
+void avr_hid_wifi_pd(int high)
{
- unsigned char lcm_sleep[4] = {CMD_SYNC, CMD_LCM_POWER, LCM_POWER_SLEEP, CMD_CLOSE};
+ uint8_t state = high ? 0x01 : 0x00;
+ avr_execute_command(CMD_WIFI_PD, &state, sizeof(state));
+}
- spi_txrx(lcm_sleep, NULL, sizeof(lcm_sleep));
+static void avr_hid_lcm_power(uint8_t parameter)
+{
+ avr_execute_command(CMD_LCM_POWER, &parameter, sizeof(parameter));
}
+void avr_hid_lcm_sleep(void)
+{
+ avr_hid_lcm_power(LCM_POWER_SLEEP);
+}
void avr_hid_lcm_wake(void)
{
- unsigned char lcm_wake[4] = {CMD_SYNC, CMD_LCM_POWER, LCM_POWER_WAKE, CMD_CLOSE};
-
- spi_txrx(lcm_wake, NULL, sizeof(lcm_wake));
+ avr_hid_lcm_power(LCM_POWER_WAKE);
}
void avr_hid_lcm_power_on(void)
{
- unsigned char lcm_power_on[4] = {CMD_SYNC, CMD_LCM_POWER, LCM_POWER_ON, CMD_CLOSE};
-
- spi_txrx(lcm_power_on, NULL, sizeof(lcm_power_on));
+ avr_hid_lcm_power(LCM_POWER_ON);
}
void avr_hid_lcm_power_off(void)
{
- unsigned char lcm_power_off[4] = {CMD_SYNC, CMD_LCM_POWER, LCM_POWER_OFF, CMD_CLOSE};
-
- spi_txrx(lcm_power_off, NULL, sizeof(lcm_power_off));
+ avr_hid_lcm_power(LCM_POWER_OFF);
}
void avr_hid_reset_codec(void)
{
- unsigned char codec_reset[4] = {CMD_SYNC, CMD_CODEC_RESET, CMD_CLOSE, CMD_FILL};
+ avr_execute_command(CMD_CODEC_RESET, NULL, 0);
+}
+
+void avr_hid_set_amp_enable(uint8_t enable)
+{
+ avr_execute_command(CMD_AMP_ENABLE, &enable, sizeof(enable));
+}
- spi_txrx(codec_reset, NULL, sizeof(codec_reset));
+static void avr_hid_sys_ctrl(uint8_t parameter)
+{
+ avr_execute_command(CMD_SYS_CTRL, &parameter, sizeof(parameter));
}
void avr_hid_power_off(void)
{
- unsigned char prg[4] = {CMD_SYNC, CMD_SYS_CTRL, SYS_CTRL_POWEROFF, CMD_CLOSE};
+ /* Do not execute command directly here because we can get called inside
+ * tick task context that must not acquire mutex.
+ */
+ queue_post(&avr_queue, POWER_OFF_REQUEST, 0);
+}
- spi_txrx(prg, NULL, sizeof(prg));
+static bool avr_state_changed(void)
+{
+ return (IO_GIO_BITSET0 & 0x1) ? false : true;
}
-#ifndef BOOTLOADER
-void btn_thread(void)
+static bool headphones_inserted(void)
+{
+ return (IO_GIO_BITSET0 & 0x04) ? false : true;
+}
+
+static void set_audio_output(bool headphones)
+{
+#ifdef BOOTLOADER
+ (void)headphones;
+#else
+ if (headphones)
+ {
+ /* Stereo output on headphones */
+ avr_hid_set_amp_enable(0);
+ aic3x_switch_output(true);
+ }
+ else
+ {
+ /* Mono output on built-in speaker */
+ aic3x_switch_output(false);
+ avr_hid_set_amp_enable(1);
+ }
+#endif
+}
+
+static void read_monotime_offset(void)
+{
+ int fd = open(MONOTIME_OFFSET_FILE, O_RDONLY);
+ if (fd >= 0)
+ {
+ uint32_t offset;
+ if (sizeof(offset) == read(fd, &offset, sizeof(offset)))
+ {
+ monotime_offset = offset;
+ }
+ close(fd);
+ }
+}
+
+static bool write_monotime_offset(void)
+{
+ bool success = false;
+ int fd = open(MONOTIME_OFFSET_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (fd >= 0)
+ {
+ uint32_t offset = monotime_offset;
+ if (sizeof(monotime_offset) == write(fd, &offset, sizeof(offset)))
+ {
+ success = true;
+ }
+ close(fd);
+ }
+ return success;
+}
+
+static void read_monotime(void)
+{
+ uint8_t tmp[4];
+ uint32_t t1, t2;
+
+ if (!avr_execute_command(CMD_MONOTIME, tmp, sizeof(tmp)))
+ {
+ return;
+ }
+ t1 = (tmp[0]) | (tmp[1] << 8) | (tmp[2] << 16) | (tmp[3] << 24);
+
+ if (!avr_execute_command(CMD_MONOTIME, tmp, sizeof(tmp)))
+ {
+ return;
+ }
+ t2 = (tmp[0]) | (tmp[1] << 8) | (tmp[2] << 16) | (tmp[3] << 24);
+
+ if ((t1 == t2) || (t1 + 1 == t2))
+ {
+ int flags = disable_irq_save();
+ monotime_value = t2;
+ monotime_value_tick = current_tick;
+ restore_irq(flags);
+ monotime_available = true;
+ }
+}
+
+static time_t get_timestamp(void)
+{
+ time_t timestamp;
+ if (!monotime_available)
+ {
+ read_monotime();
+ }
+ if (monotime_available)
+ {
+ int flags = disable_irq_save();
+ timestamp = monotime_value;
+ timestamp += monotime_offset;
+ timestamp += ((current_tick - monotime_value_tick) / HZ);
+ restore_irq(flags);
+ return timestamp;
+ }
+ return 0;
+}
+
+void rtc_init(void)
+{
+ /* This is called before disk is mounted */
+}
+
+int rtc_read_datetime(struct tm *tm)
+{
+ time_t time = get_timestamp();
+ gmtime_r(&time, tm);
+ return 1;
+}
+
+int rtc_write_datetime(const struct tm *tm)
+{
+ time_t offset = mktime((struct tm *)tm);
+ int flags = disable_irq_save();
+ offset -= monotime_value;
+ offset -= ((current_tick - monotime_value_tick) / HZ);
+ monotime_offset = offset;
+ restore_irq(flags);
+ queue_post(&avr_queue, MONOTIME_OFFSET_UPDATE, 0);
+ return 1;
+}
+
+void avr_thread(void)
{
struct queue_event ev;
+ bool headphones_active_state = headphones_inserted();
+ bool headphones_state;
+ bool disk_access_available = true;
+ bool monotime_offset_update_pending = false;
+
+ set_audio_output(headphones_active_state);
+ read_monotime_offset();
+ read_monotime();
while (1)
{
- queue_wait(&btn_queue, &ev);
-
- /* Ignore all messages except BTN_INTERRUPT */
- if (ev.id != BTN_INTERRUPT)
- continue;
+ if (avr_state_changed())
+ {
+ /* We have to read AVR state, simply check if there's any event
+ * pending but do not block. It is possible that AVR interrupt
+ * line is held active even though we read the state (change
+ * occured during read).
+ */
+ queue_wait_w_tmo(&avr_queue, &ev, 0);
+ }
+ else
+ {
+ queue_wait(&avr_queue, &ev);
+ }
- /* Enable back button interrupt */
- IO_INTC_EINT1 |= INTR_EINT1_EXT0;
+ if (ev.id == SYS_USB_CONNECTED)
+ {
+ /* Allow USB to gain exclusive storage access */
+ usb_acknowledge(SYS_USB_CONNECTED_ACK);
+ disk_access_available = false;
+ }
+ else if (ev.id == SYS_USB_DISCONNECTED)
+ {
+ disk_access_available = true;
+ }
+ else if (ev.id == MONOTIME_OFFSET_UPDATE)
+ {
+ monotime_offset_update_pending = true;
+ }
+ else if (ev.id == POWER_OFF_REQUEST)
+ {
+ avr_hid_reset_codec();
+ avr_hid_sys_ctrl(SYS_CTRL_POWEROFF);
+ }
- /* Read buttons state */
- avr_hid_get_state();
+ input_interrupt_pending = false;
+ if (avr_state_changed())
+ {
+ /* Read buttons state */
+ avr_hid_get_state();
+ }
- yield();
+ headphones_state = headphones_inserted();
+ if (headphones_state != headphones_active_state)
+ {
+ set_audio_output(headphones_state);
+ headphones_active_state = headphones_state;
+ }
- if (queue_empty(&btn_queue) && ((IO_GIO_BITSET0 & 0x1) == 0))
+ if (disk_access_available)
{
- /* for some reason we have lost next interrupt */
- queue_post(&btn_queue, BTN_INTERRUPT, 0);
+ if (monotime_offset_update_pending && write_monotime_offset())
+ {
+ monotime_offset_update_pending = false;
+ }
+ }
+
+ /* Update buffered monotime value every hour */
+ if (TIME_AFTER(current_tick, monotime_value_tick + 3600 * HZ))
+ {
+ read_monotime();
}
}
}
@@ -400,60 +890,50 @@ void GIO0(void)
{
/* Clear interrupt */
IO_INTC_IRQ1 = (1 << 5);
- /* Disable interrupt */
- IO_INTC_EINT1 &= ~INTR_EINT1_EXT0;
-
- /* interrupt will be enabled back after button read */
- queue_post(&btn_queue, BTN_INTERRUPT, 0);
-}
-
-static int headphones_inserted_callback(struct timeout *tmo)
-{
- (void)tmo;
- if (IO_GIO_BITSET0 & 0x04)
+ if (!input_interrupt_pending)
{
- aic3x_switch_output(false);
+ input_interrupt_pending = true;
+ queue_post(&avr_queue, INPUT_INTERRUPT, 0);
}
- else
- {
- aic3x_switch_output(true);
- }
-
- return 0;
}
void GIO2(void) __attribute__ ((section(".icode")));
void GIO2(void)
{
- static struct timeout headphones_oneshot;
-
/* Clear interrupt */
IO_INTC_IRQ1 = (1 << 7);
- timeout_register(&headphones_oneshot, headphones_inserted_callback,
- HZ/2, 0);
+ /* Prevent event queue overflow by allowing just one pending event */
+ if (!input_interrupt_pending)
+ {
+ input_interrupt_pending = true;
+ queue_post(&avr_queue, INPUT_INTERRUPT, 0);
+ }
}
-#endif
void button_init_device(void)
{
btn = 0;
hold_switch = false;
-#ifndef BOOTLOADER
- queue_init(&btn_queue, true);
- create_thread(btn_thread, btn_stack, sizeof(btn_stack), 0,
- btn_thread_name IF_PRIO(, PRIORITY_USER_INTERFACE)
+
+ queue_init(&avr_queue, true);
+ input_interrupt_pending = true;
+ queue_post(&avr_queue, INPUT_INTERRUPT, 0);
+ create_thread(avr_thread, avr_stack, sizeof(avr_stack), 0,
+ avr_thread_name IF_PRIO(, PRIORITY_USER_INTERFACE)
IF_COP(, CPU));
-#endif
+
IO_GIO_DIR0 |= 0x01; /* Set GIO0 as input */
+ /* Get in sync with AVR */
+ avr_hid_sync();
+
/* Enable wheel */
avr_hid_enable_wheel();
/* Read button status and tell avr we want interrupt on next change */
avr_hid_get_state();
-#ifndef BOOTLOADER
IO_GIO_IRQPORT |= 0x05; /* Enable GIO0/GIO2 external interrupt */
IO_GIO_INV0 &= ~0x05; /* Clear INV for GIO0/GIO2 */
/* falling edge detection on GIO0, any edge on GIO2 */
@@ -461,7 +941,6 @@ void button_init_device(void)
/* Enable GIO0 and GIO2 interrupts */
IO_INTC_EINT1 |= INTR_EINT1_EXT0 | INTR_EINT1_EXT2;
-#endif
}
int button_read_device(void)
@@ -476,9 +955,3 @@ bool button_hold(void)
{
return hold_switch;
}
-
-void lcd_enable(bool on)
-{
- (void)on;
-}
-
diff --git a/firmware/target/arm/tms320dm320/sansa-connect/avr-sansaconnect.h b/firmware/target/arm/tms320dm320/sansa-connect/avr-sansaconnect.h
index 06fd2b2d72..ed23e84936 100644
--- a/firmware/target/arm/tms320dm320/sansa-connect/avr-sansaconnect.h
+++ b/firmware/target/arm/tms320dm320/sansa-connect/avr-sansaconnect.h
@@ -24,11 +24,42 @@
#include "config.h"
-void avr_hid_sync(void);
+/* HDQ (bq27000) RAM registers */
+#define HDQ_REG_CTRL 0x00
+#define HDQ_REG_MODE 0x01
+#define HDQ_REG_AR 0x02
+#define HDQ_REG_ARTTE 0x04
+#define HDQ_REG_TEMP 0x06
+#define HDQ_REG_VOLT 0x08
+#define HDQ_REG_FLAGS 0x0A
+#define HDQ_REG_RSOC 0x0B
+#define HDQ_REG_NAC 0x0C
+#define HDQ_REG_CACD 0x0E
+#define HDQ_REG_CACT 0x10
+#define HDQ_REG_LMD 0x12
+#define HDQ_REG_AI 0x14
+#define HDQ_REG_TTE 0x16
+#define HDQ_REG_TTF 0x18
+#define HDQ_REG_SI 0x1A
+#define HDQ_REG_STTE 0x1C
+#define HDQ_REG_MLI 0x1E
+#define HDQ_REG_MLTTE 0x20
+#define HDQ_REG_SAE 0x22
+#define HDQ_REG_AP 0x24
+#define HDQ_REG_TTECP 0x26
+#define HDQ_REG_CYCL 0x28
+#define HDQ_REG_CYCT 0x2A
+#define HDQ_REG_CSOC 0x2C
+
void avr_hid_init(void);
+int avr_hid_hdq_read_byte(uint8_t address);
+int avr_hid_hdq_read_short(uint8_t address);
+
void avr_hid_enable_charger(void);
+void avr_hid_wifi_pd(int high);
+
void avr_hid_lcm_sleep(void);
void avr_hid_lcm_wake(void);
void avr_hid_lcm_power_on(void);
diff --git a/firmware/target/arm/tms320dm320/sansa-connect/backlight-sansaconnect.c b/firmware/target/arm/tms320dm320/sansa-connect/backlight-sansaconnect.c
index c65d806635..37cb672738 100644
--- a/firmware/target/arm/tms320dm320/sansa-connect/backlight-sansaconnect.c
+++ b/firmware/target/arm/tms320dm320/sansa-connect/backlight-sansaconnect.c
@@ -52,13 +52,12 @@ static void _backlight_write_brightness(int brightness)
void backlight_hw_on(void)
{
-#ifdef HAVE_LCD_SLEEP
if (!lcd_active())
{
lcd_awake();
lcd_update();
}
-#endif
+
/* set GIO34 as PWM1 */
IO_GIO_FSEL3 = (IO_GIO_FSEL3 & 0xFFF3) | (1 << 2);
diff --git a/firmware/target/arm/tms320dm320/sansa-connect/crt0-board.S b/firmware/target/arm/tms320dm320/sansa-connect/crt0-board.S
index a356016fb4..1848143fc1 100644
--- a/firmware/target/arm/tms320dm320/sansa-connect/crt0-board.S
+++ b/firmware/target/arm/tms320dm320/sansa-connect/crt0-board.S
@@ -58,10 +58,10 @@ _init_board:
/* Setup the EMIF interface timings */
/* FLASH interface:
- * These are based on the OF setup
+ * These are based on the OF setup
*/
/* IO_EMIF_CS0CTRL1 and
- * IO_EMIF_CS0CTRL2
+ * IO_EMIF_CS0CTRL2
*/
mwh 0x30A00, 0x889A
mwh 0x30A02, 0x1110
@@ -75,7 +75,7 @@ _init_board:
mwh 0x30A0E, 0x0222
/* IO_EMIF_CS3CTRL1 and
- * IO_EMIF_CS3CTRL2
+ * IO_EMIF_CS3CTRL2
*/
mwh 0x30A10, 0x8899
mwh 0x30A12, 0x5110
@@ -96,37 +96,49 @@ _init_board:
_clock_setup:
/* Clock initialization */
+ /* Disable peripheral clocks */
+ mwhm 0x3089A, 0
+ mwhm 0x3089C, 0
+
/* IO_CLK_BYP: Bypass the PLLs for the following changes */
mwh 0x30894, 0x1111
/*
- * IO_CLK_PLLA
- * IO_CLK_PLLB
+ * IO_CLK_PLLA: 27 MHz * 11 / 1 = 297 MHz
+ * IO_CLK_PLLB: 27 MHz
*/
- mwhm 0x30880, 0x00A0
+ mwh 0x30880, 0x10A0
mwhm 0x30882, 0x1000
- /* IO_CLK_SEL0 */
+ /* IO_CLK_SEL0: Timer 0 and 1, UART 0 and 1 from PLLIN (27 MHz) */
mwh 0x30884, 0x0066
- /* IO_CLK_SEL1 */
+ /* IO_CLK_SEL1: VENC from PLLA, OSD clock = VENC clock / 2 */
mwhm 0x30886, 0x0003
- # IO_CLK_SEL2: ARM, AXL, SDRAM and DSP are from PLLA */
+ /* IO_CLK_SEL2: ARM, AXL, SDRAM and DSP are from PLLA */
mwh 0x30888, 0
- /* IO_CLK_DIV0: Set the slow clock speed for the ARM/AHB */
+ /* IO_CLK_DIV0: Set the fast clock speed for the ARM/AHB
+ * ARM = PLLA / 2 = 148.5 MHz
+ * AHB = ARM / 2 = 74.25 MHz
+ */
mwh 0x3088A, 0x0101
- /* IO_CLK_DIV1: Accelerator, SDRAM */
+ /* IO_CLK_DIV1: Accelerator, SDRAM
+ * AXL = PLLA / 2 = 148.5 MHz
+ * SDRAM = PLLA / 3 = 99 MHz
+ */
mwh 0x3088C, 0x0102
/* IO_CLK_DIV2: DSP, MS Clock
* OF must be booted with this value
+ * DSP = PLLA / 3 = 99 MHz
+ * MS = PLLA / 1 = 297 MHz
*/
mwhm 0x3088E, 0x0200
- # PLLA &= ~0x1000 (BIC #0x1000)
+ /* PLLA &= ~0x1000 (BIC #0x1000) */
mrh 0x30880
bic r0, r0, #0x1000
strh r0, [r1]
@@ -141,25 +153,27 @@ _plla_wait:
/* IO_CLK_BYP: Enable PLL feeds */
mwhm 0x30894, 0x0
- /* IO_CLK_MOD0 */
+ /* IO_CLK_MOD0
+ * Enable clocks:
+ * ARM, Bus Controller, AHB, ARM internal memory, EMIF, SDRAM
+ * Disable clocks:
+ * ETM, E2ICE, INTC, EXTHOST, DSP, HPIB
+ */
mwh 0x30898, 0x01A7
- /* IO_CLK_MOD1 */
- mwhm 0x3089A, 0x18
-
- /* IO_CLK_MOD2 */
- mwhm 0x3089C, 0x4A0
+ /* IO_CLK_MOD2: Enable GIO and SIF1 clocks */
+ mwhm 0x3089C, 0x0420
/* Setup the SDRAM range on the AHB bus */
/* SDRAMSA */
mov r0, #0x60000
mov r1, #0x1000000
str r1, [r0, #0xF00]
-
+
/* SDRAMEA: 64MB */
mov r1, #0x5000000
str r1, [r0, #0xF04]
-
+
/* SDRC_REFCTL */
mwh 0x309A8, 0
@@ -183,8 +197,11 @@ _plla_wait:
mwhm 0x309A8, 0x0140
+ /* IMGBUF SDRAM priority bit 2 set */
mwhm 0x309BE, 0x4
+ /* SDRAM refresh priority bit 1 set */
mwhm 0x309BC, 0x2
+ /* Use defined priority bits */
ldr r0, =0x309C4
ldr r1, [r0]
orr r1, r1, #1
@@ -198,10 +215,11 @@ _plla_wait:
orr r1, r1, #0x40
strh r1, [r0]
+ /* Enable auto refresh with interval (64 + 1) * 8 SDRAM clocks */
mwhm 0x309A8, 0x0140
/* Go through the GPIO initialization */
- /* Warning: setting some of the functions wrong will make OF unable
+ /* Warning: setting some of the functions wrong will make OF unable
to boot (freeze during startup) */
/* IO_GIO_FSEL0: Set up the GPIO pin functions 0-16 */
mwhm 0x305A4, 0xC000
@@ -223,7 +241,7 @@ _plla_wait:
/* IO_GIO_DIR2 */
mwh 0x30584, 0x01FD
-
+
/* IO_GIO_INV0 */
mwh 0x30586, 0x0000
@@ -235,6 +253,6 @@ _plla_wait:
bx lr
-.ltorg
+.ltorg
.size _init_board, .-_init_board
diff --git a/firmware/target/arm/tms320dm320/sansa-connect/cryptomem-sansaconnect.c b/firmware/target/arm/tms320dm320/sansa-connect/cryptomem-sansaconnect.c
new file mode 100644
index 0000000000..ac01525500
--- /dev/null
+++ b/firmware/target/arm/tms320dm320/sansa-connect/cryptomem-sansaconnect.c
@@ -0,0 +1,80 @@
+/***************************************************************************
+* __________ __ ___.
+* Open \______ \ ____ ____ | | _\_ |__ _______ ___
+* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+* \/ \/ \/ \/ \/
+* $Id: $
+*
+* Copyright (C) 2021 by Tomasz Moń
+*
+* 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 <ctype.h>
+#include "cryptomem-sansaconnect.h"
+#include "i2c-dm320.h"
+
+/* Command values */
+#define WRITE_USER_ZONE 0xB0
+#define READ_USER_ZONE 0xB2
+#define SYSTEM_WRITE 0xB4
+#define SYSTEM_READ 0xB6
+#define VERIFY_CRYPTO 0xB8
+#define VERIFY_PASSWORD 0xBA
+
+/* SYSTEM_WRITE ADDR 1 values */
+#define WRITE_CONFIG 0x00
+#define WRITE_FUSES 0x01
+#define SEND_CHECKSUM 0x02
+#define SET_USER_ZONE 0x03
+
+int cryptomem_read_deviceid(char deviceid[32])
+{
+ int ret;
+ unsigned int i;
+ unsigned char cmd_data[3];
+
+ /* It is assumed that other I2C communication has already taken place
+ * (e.g. power_init()) before this function is called and thus we don't
+ * have to send atleast 5 dummy clock cycles here.
+ */
+
+ cmd_data[0] = SET_USER_ZONE;
+ cmd_data[1] = 0;
+ cmd_data[2] = 0;
+ ret = i2c_write(SYSTEM_WRITE, cmd_data, sizeof(cmd_data));
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ cmd_data[0] = 0;
+ cmd_data[1] = 0;
+ cmd_data[2] = 32;
+ ret = i2c_write_read_bytes(READ_USER_ZONE, cmd_data, sizeof(cmd_data),
+ deviceid, 32);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Verify that deviceid contains only printable ASCII characters */
+ for (i = 0; i < 32; i++)
+ {
+ if (!isprint(deviceid[i]))
+ {
+ return -1;
+ }
+ }
+
+ return 0;
+}
diff --git a/firmware/target/arm/tcc77x/kernel-tcc77x.c b/firmware/target/arm/tms320dm320/sansa-connect/cryptomem-sansaconnect.h
index bbfc29d915..b6b52d3b2c 100644
--- a/firmware/target/arm/tcc77x/kernel-tcc77x.c
+++ b/firmware/target/arm/tms320dm320/sansa-connect/cryptomem-sansaconnect.h
@@ -5,9 +5,9 @@
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
-* $Id$
+* $Id: $
*
-* Copyright (C) 2008 by Rob Purchase
+* Copyright (C) 2021 by Tomasz Moń
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -19,29 +19,9 @@
*
****************************************************************************/
-#include "config.h"
-#include "system.h"
-#include "kernel.h"
-#include "timer.h"
-#include "thread.h"
+#ifndef _CRYPTOMEM_SANSACONNECT_H_
+#define _CRYPTOMEM_SANSACONNECT_H_
-void tick_start(unsigned int interval_in_ms)
-{
- /* configure Timer T-Clock to 2Mhz (clock source 4 (Xin) divided by 6) */
- PCLKCFG4 = (1 << 31) | (4 << 28) | (5 << 16);
+int cryptomem_read_deviceid(char deviceid[32]);
- /* disable Timer0 */
- TCFG0 &= ~1;
-
- /* set counter reference value based on 1Mhz tick */
- TREF0 = interval_in_ms * 1000;
-
- /* Timer0 = reset to 0, divide=2, IRQ enable, enable (continuous) */
- TCFG0 = (1<<8) | (0<<4) | (1<<3) | 1;
-
- /* Unmask timer IRQ */
- IEN |= TIMER0_IRQ_MASK;
-}
-
-/* NB: Since we are using a single timer IRQ, tick tasks are dispatched as
- part of the central timer IRQ processing in timer-tcc77x.c */
+#endif /* _CRYPTOMEM_SANSACONNECT_H_ */
diff --git a/firmware/target/arm/tms320dm320/sansa-connect/lcd-sansaconnect.c b/firmware/target/arm/tms320dm320/sansa-connect/lcd-sansaconnect.c
index 27eb0b407a..9674f07a64 100644
--- a/firmware/target/arm/tms320dm320/sansa-connect/lcd-sansaconnect.c
+++ b/firmware/target/arm/tms320dm320/sansa-connect/lcd-sansaconnect.c
@@ -29,60 +29,110 @@
#include "lcd.h"
#include "lcd-target.h"
#include "avr-sansaconnect.h"
+#include "backlight-target.h"
-extern bool lcd_on; /* lcd-memframe.c */
+/* See lcd-memframe.c */
+extern void lcd_set_active(bool active);
+
+static bool lcd_powered;
-#if defined(HAVE_LCD_SLEEP)
void lcd_sleep(void)
{
- if (lcd_on)
+ if (lcd_powered)
{
- lcd_on = false;
+ lcd_set_active(false);
- avr_hid_lcm_sleep();
- sleep(HZ/20);
+ avr_hid_lcm_power_off();
+ mdelay(100);
+ /* Disable OSD window */
+ bitclr16(&IO_OSD_OSDWINMD0, 0x01);
/* disable video encoder */
- bitclr16(&IO_VID_ENC_VMOD, 0x01);
+ bitclr16(&IO_VID_ENC_VMOD, VENC_VMOD_VENC);
+ mdelay(66);
- sleep(HZ/20);
+ /* disable video encoder and OSD clocks */
+ bitclr16(&IO_CLK_MOD1, CLK_MOD1_VENC | CLK_MOD1_OSD);
- /* disable video encoder clock */
- bitclr16(&IO_CLK_MOD1, CLK_MOD1_VENC);
+ lcd_powered = false;
}
}
void lcd_awake(void)
{
- if (!lcd_on)
+ if (!lcd_powered)
{
- lcd_on = true;
+ /* Enable Video Encoder and OSD clocks */
+ bitset16(&IO_CLK_MOD1, CLK_MOD1_VENC | CLK_MOD1_OSD);
+ /* Enable OSD window */
+ bitset16(&IO_OSD_OSDWINMD0, 0x01);
+ /* Enable video encoder */
+ bitset16(&IO_VID_ENC_VMOD, VENC_VMOD_VENC);
+
+ avr_hid_lcm_power_on();
+ mdelay(66);
+ lcd_set_active(true);
+ avr_hid_lcm_wake();
- /* enable video encoder clock */
- bitset16(&IO_CLK_MOD1, CLK_MOD1_VENC);
+ lcd_powered = true;
+ send_event(LCD_EVENT_ACTIVATION, NULL);
+ }
+}
- /* enable video encoder */
- bitset16(&IO_VID_ENC_VMOD, 0x01);
+void lcd_shutdown(void)
+{
+ backlight_hw_off();
+ lcd_sleep();
+}
+
+void lcd_enable(bool enable)
+{
+ if (lcd_active() == enable)
+ return;
+
+ lcd_set_active(enable);
+
+ if (enable)
+ {
+ /* Enable Video Encoder and OSD clocks */
+ bitset16(&IO_CLK_MOD1, CLK_MOD1_VENC | CLK_MOD1_OSD);
+ /* Enable OSD window */
+ bitset16(&IO_OSD_OSDWINMD0, 0x01);
+ /* Enable video encoder */
+ bitset16(&IO_VID_ENC_VMOD, VENC_VMOD_VENC);
avr_hid_lcm_wake();
+ mdelay(30);
send_event(LCD_EVENT_ACTIVATION, NULL);
+ }
+ else
+ {
+ mdelay(30);
+ avr_hid_lcm_sleep();
+ mdelay(10);
- lcd_update();
+ /* Disable OSD window */
+ bitclr16(&IO_OSD_OSDWINMD0, 0x01);
+ /* disable video encoder */
+ bitclr16(&IO_VID_ENC_VMOD, VENC_VMOD_VENC);
+ mdelay(66);
+
+ /* disable video encoder and OSD clocks */
+ bitclr16(&IO_CLK_MOD1, CLK_MOD1_VENC | CLK_MOD1_OSD);
}
}
-#endif
void lcd_init_device(void)
{
unsigned int addr;
-
+
/* Disable Video Encoder clock */
bitclr16(&IO_CLK_MOD1, CLK_MOD1_VENC);
/* configure GIO39, GIO34 as outputs */
IO_GIO_DIR2 &= ~((1 << 7) /* GIO39 */ | (1 << 2) /* GIO34 */);
-
+
IO_GIO_FSEL3 = (IO_GIO_FSEL3 & ~(0x300F)) |
(0x1000) /* GIO39 - FIELD_VENC */ |
(0x4); /* GIO34 - PWM1 (brightness control) */
@@ -92,7 +142,7 @@ void lcd_init_device(void)
VENC Clock from PLLA */
IO_CLK_SEL1 = 0x3;
- /* Set VENC Clock Division to 11
+ /* Set VENC Clock Division to 11 (PLLA / 11 = 297 MHz / 11 = 27 MHz)
OF bootloader sets division to 8, vmlinux sets it to 11 */
IO_CLK_DIV3 = (IO_CLK_DIV3 & ~(0x1F00)) | 0xB00;
@@ -121,11 +171,9 @@ void lcd_init_device(void)
IO_VID_ENC_VMOD = 0x2015; /* OF sets 0x2011 */
/* Copy Rockbox frame buffer to the second framebuffer */
+ lcd_set_active(true);
lcd_update();
- avr_hid_sync();
- avr_hid_lcm_power_on();
-
/* set framebuffer address - OF sets RAM start address to 0x1000000 */
addr = ((int)FRAME-CONFIG_SDRAM_START)/32;
@@ -150,8 +198,9 @@ void lcd_init_device(void)
/* Enable Video Encoder - RGB666, custom timing */
IO_VID_ENC_VMOD = 0x2015;
+
+ lcd_powered = true;
avr_hid_lcm_wake();
- lcd_on = true;
}
#ifdef LCD_USE_DMA
@@ -168,14 +217,14 @@ static void dma_lcd_copy_buffer_rect(int x, int y, int width, int height)
/* Set source and destination addresses */
dst = (char*)(FRAME + LCD_WIDTH*y + x);
src = (char*)(FBADDR(x,y));
-
+
/* Flush cache to memory */
commit_dcache();
/* Addresses are relative to start of SDRAM */
src -= CONFIG_SDRAM_START;
dst -= CONFIG_SDRAM_START;
-
+
/* Enable Image Buffer clock */
bitset16(&IO_CLK_MOD1, CLK_MOD1_IMGBUF);
@@ -192,7 +241,7 @@ static void dma_lcd_copy_buffer_rect(int x, int y, int width, int height)
/* Set the start address of buffer */
COP_BUF_ADDR = 0x0000;
-
+
/* Setup SDRAM stride */
COP_SDEM_LOFST = LCD_WIDTH;
@@ -201,28 +250,28 @@ static void dma_lcd_copy_buffer_rect(int x, int y, int width, int height)
addr = (int)src;
addr >>= 1; /* Addresses are in 16-bit words */
-
+
/* Setup the registers to initiate the read from SDRAM */
COP_SDEM_ADDRH = addr >> 16;
COP_SDEM_ADDRL = addr & 0xFFFF;
-
+
/* Set direction and start */
COP_DMA_CTRL = 0x0001;
COP_DMA_CTRL |= 0x0002;
/* Wait for read to finish */
while (COP_DMA_CTRL & 0x02) {};
-
+
addr = (int)dst;
addr >>= 1;
-
+
COP_SDEM_ADDRH = addr >> 16;
COP_SDEM_ADDRL = addr & 0xFFFF;
-
+
/* Set direction and start transfer */
COP_DMA_CTRL = 0x0000;
COP_DMA_CTRL |= 0x0002;
-
+
/* Wait for the transfer to complete */
while (COP_DMA_CTRL & 0x02) {};
@@ -247,7 +296,7 @@ void lcd_update_rect(int x, int y, int width, int height)
__attribute__ ((section(".icode")));
void lcd_update_rect(int x, int y, int width, int height)
{
- if (!lcd_on)
+ if (!lcd_active())
return;
if ((width | height) < 0)
@@ -271,7 +320,7 @@ void lcd_update_rect(int x, int y, int width, int height)
void lcd_update(void) __attribute__ ((section(".icode")));
void lcd_update(void)
{
- if (!lcd_on)
+ if (!lcd_active())
return;
lcd_update_rect(0, 0, LCD_WIDTH, LCD_HEIGHT);
diff --git a/firmware/target/arm/tms320dm320/sansa-connect/pcm-sansaconnect.c b/firmware/target/arm/tms320dm320/sansa-connect/pcm-sansaconnect.c
index f9f19a8045..bda000e68f 100644
--- a/firmware/target/arm/tms320dm320/sansa-connect/pcm-sansaconnect.c
+++ b/firmware/target/arm/tms320dm320/sansa-connect/pcm-sansaconnect.c
@@ -42,22 +42,6 @@ void pcm_play_dma_postinit(void)
audiohw_postinit();
}
-/* Return the current location in the SDRAM to SARAM transfer along with the
- * number of bytes read in the current buffer (count). There is latency with
- * this method equivalent to ~ the size of the SARAM buffer since there is
- * another buffer between your ears and this calculation, but this works for
- * key clicks and an approximate peak meter.
- */
-const void * pcm_play_dma_get_peak_buffer(int *count)
-{
- int cnt = DSP_(_sdem_level);
-
- unsigned long addr = (unsigned long) start + cnt;
-
- *count = (cnt & 0xFFFFF) >> 1;
- return (void *)((addr + 2) & ~3);
-}
-
void pcm_play_dma_init(void)
{
/* GIO16 is DSP/AIC3X CLK */
diff --git a/firmware/target/arm/tms320dm320/sansa-connect/power-sansaconnect.c b/firmware/target/arm/tms320dm320/sansa-connect/power-sansaconnect.c
index ecdf3b1a75..597fb6b7e0 100644
--- a/firmware/target/arm/tms320dm320/sansa-connect/power-sansaconnect.c
+++ b/firmware/target/arm/tms320dm320/sansa-connect/power-sansaconnect.c
@@ -31,6 +31,29 @@
#include "i2c-dm320.h"
#include "logf.h"
+
+const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] =
+{
+ 3450
+};
+
+const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] =
+{
+ 3400
+};
+
+/* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */
+const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11] =
+{
+ { 3400, 3508, 3630, 3703, 3727, 3750, 3803, 3870, 3941, 4026, 4142 }
+};
+
+/* voltages (millivolt) of 0%, 10%, ... 100% when charging enabled */
+const unsigned short percent_to_volt_charge[11] =
+{
+ 3540, 3788, 3860, 3890, 3916, 3956, 4016, 4085, 4164, 4180, 4190
+};
+
/* (7-bit) address is 0x48, the LSB is read/write flag */
#define TPS65021_ADDR (0x48 << 1)
@@ -54,7 +77,7 @@ void power_init(void)
/* PWM mode */
tps65021_write_reg(0x04, 0xB2);
-
+
/* Set core voltage to 1.5V */
tps65021_write_reg(0x06, 0x1C);
@@ -70,25 +93,10 @@ void power_off(void)
/* Disable GIO0 and GIO2 interrupts */
IO_INTC_EINT1 &= ~(INTR_EINT1_EXT2 | INTR_EINT1_EXT0);
- avr_hid_reset_codec();
avr_hid_power_off();
}
-#if CONFIG_CHARGING
-unsigned int power_input_status(void)
-{
- return POWER_INPUT_NONE;
-}
-
-/* Returns true if the unit is charging the batteries. */
-bool charging_state(void)
-{
- return false;
-}
-#endif
-
void ide_power_enable(bool on)
{
(void)on;
}
-
diff --git a/firmware/target/arm/tms320dm320/sansa-connect/powermgmt-sansaconnect.c b/firmware/target/arm/tms320dm320/sansa-connect/powermgmt-sansaconnect.c
deleted file mode 100644
index 8b3f05107c..0000000000
--- a/firmware/target/arm/tms320dm320/sansa-connect/powermgmt-sansaconnect.c
+++ /dev/null
@@ -1,65 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id: $
- *
- * Copyright (C) 2011 by Tomasz Moń
- *
- * 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 "adc.h"
-#include "powermgmt.h"
-#include "kernel.h"
-
-/* Use fake linear scale as AVR does the voltage to percentage conversion */
-
-static unsigned int current_battery_level = 100;
-
-/* This specifies the battery level that writes are still safe */
-const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] =
-{
- 5
-};
-
-/* Below this the player cannot be considered to operate reliably */
-const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] =
-{
- 4
-};
-
-/* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */
-const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11] =
-{
- { 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 },
-};
-
-/* voltages (millivolt) of 0%, 10%, ... 100% when charging enabled */
-const unsigned short percent_to_volt_charge[11] =
-{
- 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100,
-};
-
-/* Returns battery voltage from ADC [millivolts] */
-int _battery_voltage(void)
-{
- return current_battery_level;
-}
-
-void set_battery_level(unsigned int level)
-{
- current_battery_level = level;
-}
-
diff --git a/firmware/target/arm/tms320dm320/sansa-connect/tnetv105_cppi.c b/firmware/target/arm/tms320dm320/sansa-connect/tnetv105_cppi.c
new file mode 100644
index 0000000000..1ce6e4a640
--- /dev/null
+++ b/firmware/target/arm/tms320dm320/sansa-connect/tnetv105_cppi.c
@@ -0,0 +1,1047 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id: $
+ *
+ * Copyright (C) 2021 by Tomasz Moń
+ * Copied with minor modifications from Sansa Connect Linux driver
+ * Copyright (c) 2005,2006 Zermatt Systems, Inc.
+ * Written by: Ben Bostwick
+ *
+ * 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 <string.h>
+#include "config.h"
+#include "system.h"
+#include "kernel.h"
+#include "panic.h"
+#include "tnetv105_usb_drv.h"
+#include "tnetv105_cppi.h"
+#if USB_CPPI_LOGGING
+#define LOGF_ENABLE
+#endif
+#include "logf.h"
+
+/* This file is pretty much directly copied from the Sansa Connect
+ * Linux kernel source code. This is because the functionality is
+ * nicely separated from actual kernel specific code and CPPI seems
+ * complex (atleast without access to the datasheet).
+ *
+ * The only non cosmetic change was changing the dynamic allocations
+ * to static allocations.
+ *
+ * It seems that the only way to get interrupt on non-control endpoint
+ * acticity is to use the CPPI. This sounds like a plausible explanation
+ * for the fake DMA buffers mentioned in CPPI code.
+ */
+
+/* Translate Linux consistent_sync() to Rockbox functions */
+#define DMA_TO_DEVICE commit_discard_dcache_range
+#define DMA_FROM_DEVICE discard_dcache_range
+#define consistent_sync(ptr, size, func) func(ptr, size)
+/* Rockbox TMS320DM320 crt0.S maps everything to itself */
+#define __virt_to_phys(ptr) ptr
+#define __phys_to_virt(ptr) ptr
+
+// CPPI functions
+#define CB_SOF_BIT (1<<31)
+#define CB_EOF_BIT (1<<30)
+#define CB_OWNERSHIP_BIT (1<<29)
+#define CB_EOQ_BIT (1<<28)
+#define CB_ZLP_GARBAGE (1<<23)
+#define CB_SIZE_MASK 0x0000ffff
+#define CB_OFFSET_MASK 0xffff0000
+#define TEARDOWN_VAL 0xfffffffc
+
+#define MAX_BUF_SIZE 512
+
+#define CPPI_DMA_RX_BUF_SIZE (MAX_BUF_SIZE * CPPI_RX_NUM_BUFS)
+
+static uint8_t *dma_recv_buf[CPPI_NUM_CHANNELS];
+static uint8_t ch0_rx_buf[CPPI_DMA_RX_BUF_SIZE];
+static uint8_t ch1_rx_buf[CPPI_DMA_RX_BUF_SIZE];
+
+#if USB_CPPI_LOGGING
+#define cppi_log_event0(msg) logf(msg)
+#define cppi_log_event1(msg, data0) logf(msg " %lx", (uint32_t)data0)
+#define cppi_log_event2(msg, data0, data1) logf(msg " %lx %lx", (uint32_t)data0, (uint32_t)data1)
+#define cppi_log_event3(msg, data0, data1, data2) logf(msg " %lx %lx %lx", (uint32_t)data0, (uint32_t)data1, (uint32_t)data2)
+#define cppi_log_event4(msg, data0, data1, data2, data3) logf(msg " %lx %lx %lx %lx", (uint32_t)data0, (uint32_t)data1, (uint32_t)data2, (uint32_t)data3)
+#else
+#define cppi_log_event0(x)
+#define cppi_log_event1(x, y)
+#define cppi_log_event2(x, y, z)
+#define cppi_log_event3(x, y, z, w)
+#define cppi_log_event4(x, y, z, w, u)
+#endif
+
+/*
+ * This function processes transmit interrupts. It traverses the
+ * transmit buffer queue, detecting sent data buffers
+ *
+ * @return 0 if OK, non-zero otherwise.
+ */
+int tnetv_cppi_tx_int(struct cppi_info *cppi, int ch)
+{
+ cppi_tcb *CurrentTcb,*LastTcbProcessed;
+ uint32_t TxFrameStatus;
+ cppi_txcntl *pTxCtl = &cppi->tx_ctl[ch];
+ int bytes_sent = 0;
+
+ cppi_log_event1("[cppi]TxInt ch", ch);
+
+ CurrentTcb = pTxCtl->TxActQueueHead;
+
+ if (CurrentTcb == 0)
+ {
+ cppi_log_event0("[cppi] tx int: no current tcb");
+ return -1;
+ }
+
+ // sync up the tcb from memory
+ consistent_sync(CurrentTcb, sizeof(*CurrentTcb), DMA_FROM_DEVICE);
+
+ TxFrameStatus = CurrentTcb->mode;
+ LastTcbProcessed = NULL;
+
+ cppi_log_event3("[cppi] int tcb status", (uint32_t) CurrentTcb, TxFrameStatus, CurrentTcb->Off_BLen);
+
+ while(CurrentTcb && (TxFrameStatus & CB_OWNERSHIP_BIT) == 0)
+ {
+ cppi_log_event3("[cppi] tx int: tcb (mode) (len)", (uint32_t) CurrentTcb, CurrentTcb->mode, CurrentTcb->Off_BLen);
+
+ // calculate the amount of bytes sent.
+ // don't count the fake ZLP byte
+ if (CurrentTcb->Off_BLen > 0x1)
+ {
+ bytes_sent += CurrentTcb->Off_BLen & 0xFFFF;
+ }
+
+ if (CurrentTcb->mode & CB_EOQ_BIT)
+ {
+ if (CurrentTcb->Next)
+ {
+ cppi_log_event0(" [cppi] misqueue!");
+
+ // Misqueued packet
+ tnetv_usb_reg_write(TNETV_DMA_TX_STATE(ch, TNETV_CPPI_TX_WORD_HDP), CurrentTcb->HNext);
+ }
+ else
+ {
+ cppi_log_event0("[cppi] eoq");
+
+ /* Tx End of Queue */
+ pTxCtl->TxActive = 0;
+ }
+ }
+
+ cppi_log_event1("[cppi]SendComplete: ", CurrentTcb->Off_BLen & 0xFFFF);
+
+ // Write the completion pointer
+ tnetv_usb_reg_write(TNETV_DMA_TX_CMPL(ch), __dma_to_vlynq_phys(CurrentTcb->dma_handle));
+
+
+ LastTcbProcessed = CurrentTcb;
+ CurrentTcb = CurrentTcb->Next;
+
+ // clean up TCB fields
+ LastTcbProcessed->HNext = 0;
+ LastTcbProcessed->Next = 0;
+ LastTcbProcessed->BufPtr = 0;
+ LastTcbProcessed->Off_BLen = 0;
+ LastTcbProcessed->mode = 0;
+ LastTcbProcessed->Eop = 0;
+
+ /* Push Tcb(s) back onto the list */
+ if (pTxCtl->TcbPool)
+ {
+ LastTcbProcessed->Next = pTxCtl->TcbPool->Next;
+ pTxCtl->TcbPool->Next = LastTcbProcessed;
+ }
+ else
+ {
+ pTxCtl->TcbPool = LastTcbProcessed;
+ }
+
+ consistent_sync(LastTcbProcessed, sizeof(*LastTcbProcessed), DMA_TO_DEVICE);
+
+ // get the status of the next packet
+ if (CurrentTcb)
+ {
+ // sync up the tcb from memory
+ consistent_sync(CurrentTcb, sizeof(*CurrentTcb), DMA_FROM_DEVICE);
+
+ TxFrameStatus = CurrentTcb->mode;
+ }
+
+
+ }
+
+ pTxCtl->TxActQueueHead = CurrentTcb;
+
+ if (!LastTcbProcessed)
+ {
+ cppi_log_event1(" [cppi]No Tx packets serviced on int! ch", ch);
+ return -1;
+ }
+
+ return bytes_sent;
+}
+
+int tnetv_cppi_flush_tx_queue(struct cppi_info *cppi, int ch)
+{
+ cppi_txcntl *pTxCtl = &cppi->tx_ctl[ch];
+ cppi_tcb *tcb, *next_tcb;
+
+ tcb = pTxCtl->TxActQueueHead;
+
+ cppi_log_event1("[cppi] flush TX ", (uint32_t) pTxCtl->TxActQueueHead);
+
+ while (tcb)
+ {
+ tcb->mode = 0;
+ tcb->BufPtr = 0;
+ tcb->Off_BLen = 0;
+ tcb->Eop = 0;
+ tcb->HNext = 0;
+
+ next_tcb = tcb->Next;
+
+ tcb->Next = pTxCtl->TcbPool;
+ pTxCtl->TcbPool = tcb;
+
+ tcb = next_tcb;
+ }
+
+ pTxCtl->TxActQueueHead = 0;
+ pTxCtl->TxActQueueTail = 0;
+ pTxCtl->TxActive = 0;
+
+ return 0;
+}
+
+
+/**
+ * @ingroup CPHAL_Functions
+ * This function transmits the data in FragList using available transmit
+ * buffer descriptors. More information on the use of the Mode parameter
+ * is available in the module-specific appendices. Note: The OS should
+ * not call Send() for a channel that has been requested to be torndown.
+ *
+ */
+int tnetv_cppi_send(struct cppi_info *cppi, int ch, dma_addr_t buf, unsigned length, int send_zlp)
+{
+ cppi_txcntl *pTxCtl;
+ cppi_tcb *first_tcb;
+ cppi_tcb *tcb;
+ int queued_len;
+ dma_addr_t buf_to_send;
+ dma_addr_t buf_ptr;
+ int total_len = length;
+ int pktlen;
+
+ pTxCtl = &cppi->tx_ctl[ch];
+
+ if (length == 0)
+ {
+ cppi_log_event0("[cppi] len = 0, nothing to send");
+ return -1;
+ }
+
+ // no send buffers.. try again later
+ if (!pTxCtl->TcbPool)
+ {
+ cppi_log_event0("[cppi] out of cppi buffers");
+ return -1;
+ }
+
+ // only send 1 packet at a time
+ if (pTxCtl->TxActQueueHead || pTxCtl->TxActive)
+ {
+ cppi_log_event0("[cppi] already sending!");
+ return -1;
+ }
+
+ buf_to_send = buf;
+
+ // usb_requests can have a 32 bit length, but CPPI DMA fragments
+ // have a (64k - 1) limit. Split the usb_request up into fragments here.
+ first_tcb = pTxCtl->TcbPool;
+ tcb = first_tcb;
+
+ cppi_log_event4("[cppi]cppi_send (buf) (len) (pool) (dma)", (uint32_t) buf_to_send, total_len, (uint32_t) first_tcb, first_tcb->dma_handle);
+
+ queued_len = 0;
+
+ do
+ {
+ buf_ptr = buf_to_send + queued_len;
+ tcb->BufPtr = __dma_to_vlynq_phys(buf_ptr);
+ tcb->HNext = 0;
+
+ // can't transfer more that 64k-1 bytes in 1 CPPI transfer
+ // need to queue up transfers if it's greater than that
+ pktlen = ((total_len - queued_len) > CPPI_MAX_FRAG) ? CPPI_MAX_FRAG : (total_len - queued_len);
+ tcb->Off_BLen = pktlen;
+ tcb->mode = (CB_OWNERSHIP_BIT | CB_SOF_BIT | CB_EOF_BIT | pktlen);
+
+ queued_len += pktlen;
+
+ if (queued_len < total_len)
+ {
+ tcb->HNext = __dma_to_vlynq_phys(((cppi_tcb *) tcb->Next)->dma_handle);
+
+ // write out the buffer to memory
+ consistent_sync(tcb, sizeof(*tcb), DMA_TO_DEVICE);
+
+ cppi_log_event4("[cppi] q tcb", (uint32_t) tcb, ((uint32_t *) tcb)[0], ((uint32_t *) tcb)[1], ((uint32_t *) tcb)[2]);
+ cppi_log_event4("[cppi] ", ((uint32_t *) tcb)[3], ((uint32_t *) tcb)[4], ((uint32_t *) tcb)[5], ((uint32_t *) tcb)[6]);
+
+ tcb = tcb->Next;
+ }
+ } while (queued_len < total_len);
+
+ /* In the Tx Interrupt handler, we will need to know which TCB is EOP,
+ so we can save that information in the SOP */
+ first_tcb->Eop = tcb;
+
+ // set the secret ZLP bit if necessary, this will be a completely separate packet
+ if (send_zlp)
+ {
+#if defined(AUTO_ZLP) && AUTO_ZLP
+ // add an extra buffer at the end to hold the ZLP
+ tcb->HNext = __dma_to_vlynq_phys(((cppi_tcb *) tcb->Next)->dma_handle);
+
+ // write out the buffer to memory
+ consistent_sync(tcb, sizeof(*tcb), DMA_TO_DEVICE);
+
+ tcb = tcb->Next;
+
+ /* In the Tx Interrupt handler, we will need to know which TCB is EOP,
+ so we can save that information in the SOP */
+ first_tcb->Eop = tcb;
+#endif
+
+ buf_ptr = buf_to_send + queued_len;
+ tcb->BufPtr = __dma_to_vlynq_phys(buf_ptr); // not used, but can't be zero
+ tcb->HNext = 0;
+ tcb->Off_BLen = 0x1; // device will send (((len - 1) / maxpacket) + 1) ZLPs
+ tcb->mode = (CB_SOF_BIT | CB_EOF_BIT | CB_OWNERSHIP_BIT | CB_ZLP_GARBAGE | 0x1); // send 1 ZLP
+ tcb->Eop = tcb;
+
+ cppi_log_event0("[cppi] Send ZLP!");
+ }
+
+ pTxCtl->TcbPool = tcb->Next;
+
+ tcb->Next = 0;
+ tcb->HNext = 0;
+
+ // write out the buffer to memory
+ consistent_sync(tcb, sizeof(*tcb), DMA_TO_DEVICE);
+
+ cppi_log_event4("[cppi] q tcb", (uint32_t) tcb, ((uint32_t *) tcb)[0], ((uint32_t *) tcb)[1], ((uint32_t *) tcb)[2]);
+ cppi_log_event4("[cppi] ", ((uint32_t *) tcb)[3], ((uint32_t *) tcb)[4], ((uint32_t *) tcb)[5], ((uint32_t *) tcb)[6]);
+
+ cppi_log_event4("[cppi] send queued (ptr) (len) (ftcb, ltcb)", (uint32_t) tcb->BufPtr, tcb->Off_BLen, (uint32_t) first_tcb, (uint32_t) tcb);
+
+ /* put it on the queue */
+ pTxCtl->TxActQueueHead = first_tcb;
+ pTxCtl->TxActQueueTail = tcb;
+
+ cppi_log_event3("[cppi] setting state (head) (virt) (next)", (uint32_t) first_tcb, __dma_to_vlynq_phys(first_tcb->dma_handle), (uint32_t) first_tcb->HNext);
+
+ /* write CPPI TX HDP - cache is cleaned above */
+ tnetv_usb_reg_write(TNETV_DMA_TX_STATE(ch, TNETV_CPPI_TX_WORD_HDP), __dma_to_vlynq_phys(first_tcb->dma_handle));
+
+ pTxCtl->TxActive = 1;
+
+ return 0;
+}
+
+/*
+ * This function allocates transmit buffer descriptors (internal CPHAL function).
+ * It creates a high priority transmit queue by default for a single Tx
+ * channel. If QoS is enabled for the given CPHAL device, this function
+ * will also allocate a low priority transmit queue.
+ *
+ * @return 0 OK, Non-Zero Not OK
+ */
+int tnetv_cppi_init_tcb(struct cppi_info *cppi, int ch)
+{
+ int i, num;
+ cppi_tcb *pTcb = 0;
+ char *AllTcb;
+ int tcbSize;
+ cppi_txcntl *pTxCtl = &cppi->tx_ctl[ch];
+
+ num = pTxCtl->TxNumBuffers;
+ tcbSize = (sizeof(cppi_tcb) + 0xf) & ~0xf;
+
+ cppi_log_event4("[cppi] init_tcb (ch) (num) (dma) (tcbsz)", ch, num, pTxCtl->tcb_start_dma_addr, tcbSize);
+
+ if (pTxCtl->TxNumBuffers == 0)
+ {
+ return -1;
+ }
+
+ /* if the memory has already been allocated, simply reuse it! */
+ AllTcb = pTxCtl->TcbStart;
+
+ // now reinitialize the TCB pool
+ pTxCtl->TcbPool = 0;
+ for (i = 0; i < num; i++)
+ {
+ pTcb = (cppi_tcb *)(AllTcb + (i * tcbSize));
+ pTcb->dma_handle = pTxCtl->tcb_start_dma_addr + (i * tcbSize);
+
+ pTcb->BufPtr = 0;
+ pTcb->mode = 0;
+ pTcb->HNext = 0;
+ pTcb->Off_BLen = 0;
+ pTcb->Eop = 0;
+
+ pTcb->Next = (void *) pTxCtl->TcbPool;
+
+ pTxCtl->TcbPool = pTcb;
+ }
+
+ cppi_log_event2(" [cppi]TcbPool", (uint32_t) pTxCtl->TcbPool, pTxCtl->TcbPool->dma_handle);
+
+#if USB_CPPI_LOGGING
+ {
+ // BEN DEBUG
+ cppi_tcb *first_tcb = pTxCtl->TcbPool;
+ cppi_log_event4("[cppi] init tcb", (uint32_t) first_tcb, ((uint32_t *) first_tcb)[0], ((uint32_t *) first_tcb)[1], ((uint32_t *) first_tcb)[2]);
+ cppi_log_event4("[cppi] ", ((uint32_t *) first_tcb)[3], ((uint32_t *) first_tcb)[4], ((uint32_t *) first_tcb)[5], ((uint32_t *) first_tcb)[6]);
+ }
+#endif
+
+ return 0;
+}
+
+// BEN DEBUG
+void tnetv_cppi_dump_info(struct cppi_info *cppi)
+{
+ int ch;
+ cppi_rxcntl *pRxCtl;
+ cppi_txcntl *pTxCtl;
+ cppi_tcb *tcb;
+ cppi_rcb *rcb;
+
+ logf("CPPI struct:\n");
+ logf("Buf mem: %lx Buf size: %d int: %lx %lx\n\n", (uint32_t) cppi->dma_mem, cppi->dma_size, tnetv_usb_reg_read(TNETV_USB_RX_INT_STATUS), tnetv_usb_reg_read(VL_INTST));
+
+ for (ch = 0; ch < CPPI_NUM_CHANNELS; ch++)
+ {
+ pRxCtl = &cppi->rx_ctl[ch];
+ pTxCtl = &cppi->tx_ctl[ch];
+
+ logf("ch: %d\n", ch);
+ logf(" rx_numbufs: %d active %ld free_buf_cnt %ld\n", pRxCtl->RxNumBuffers, pRxCtl->RxActive, tnetv_usb_reg_read(TNETV_USB_RX_FREE_BUF_CNT(ch)));
+ logf(" q_cnt %ld head %lx tail %lx\n", pRxCtl->RxActQueueCount, (uint32_t) pRxCtl->RxActQueueHead, (uint32_t) pRxCtl->RxActQueueTail);
+ logf(" fake_head: %lx fake_tail: %lx\n", (uint32_t) pRxCtl->RxFakeRcvHead, (uint32_t) pRxCtl->RxFakeRcvTail);
+
+ rcb = (cppi_rcb *) pRxCtl->RcbStart;
+ do
+ {
+ if (!rcb)
+ break;
+
+ logf(" Rcb: %lx\n", (uint32_t) rcb);
+ logf(" HNext %lx BufPtr %lx Off_BLen %lx mode %lx\n", rcb->HNext, rcb->BufPtr, rcb->Off_BLen, rcb->mode);
+ logf(" Next %lx Eop %lx dma_handle %lx fake_bytes %lx\n", (uint32_t) rcb->Next, (uint32_t) rcb->Eop, rcb->dma_handle, rcb->fake_bytes);
+ rcb = rcb->Next;
+
+ } while (rcb && rcb != (cppi_rcb *) pRxCtl->RcbStart);
+
+ logf("\n");
+ logf(" tx_numbufs: %d active %ld\n", pTxCtl->TxNumBuffers, pTxCtl->TxActive);
+ logf(" q_cnt %ld head %lx tail %lx\n", pTxCtl->TxActQueueCount, (uint32_t) pTxCtl->TxActQueueHead, (uint32_t) pTxCtl->TxActQueueTail);
+
+ tcb = (cppi_tcb *) pTxCtl->TcbPool;
+ do
+ {
+ if (!tcb)
+ break;
+
+ logf(" Tcb (pool): %lx\n", (uint32_t) tcb);
+ logf(" HNext %lx BufPtr %lx Off_BLen %lx mode %lx\n", tcb->HNext, tcb->BufPtr, tcb->Off_BLen, tcb->mode);
+ logf(" Next %lx Eop %lx dma_handle %lx\n", (uint32_t) tcb->Next, (uint32_t) tcb->Eop, tcb->dma_handle);
+ tcb = tcb->Next;
+
+ } while (tcb && tcb != (cppi_tcb *) pTxCtl->TcbPool);
+
+ tcb = (cppi_tcb *) pTxCtl->TxActQueueHead;
+ do
+ {
+ if (!tcb)
+ break;
+
+ logf(" Tcb (act): %lx\n", (uint32_t) tcb);
+ logf(" HNext %lx BufPtr %lx Off_BLen %lx mode %lx\n", tcb->HNext, tcb->BufPtr, tcb->Off_BLen, tcb->mode);
+ logf(" Next %lx Eop %lx dma_handle %lx\n", (uint32_t) tcb->Next, (uint32_t) tcb->Eop, tcb->dma_handle);
+ tcb = tcb->Next;
+
+ } while (tcb && tcb != (cppi_tcb *) pTxCtl->TxActQueueTail);
+
+ }
+}
+
+/**
+ *
+ * This function is called to indicate to the CPHAL that the upper layer
+ * software has finished processing the receive data (given to it by
+ * osReceive()). The CPHAL will then return the appropriate receive buffers
+ * and buffer descriptors to the available pool.
+ *
+ */
+int tnetv_cppi_rx_return(struct cppi_info *cppi, int ch, cppi_rcb *done_rcb)
+{
+ cppi_rxcntl *pRxCtl = &cppi->rx_ctl[ch];
+ cppi_rcb *curRcb, *lastRcb, *endRcb;
+ int num_bufs = 0;
+
+ if (!done_rcb)
+ return -1;
+
+ //cppi_log_event3("[cppi] rx_return (last) (first) bufinq", (uint32_t) done_rcb, (uint32_t) done_rcb->Eop, tnetv_usb_reg_read(TNETV_USB_RX_FREE_BUF_CNT(ch)));
+
+ curRcb = done_rcb;
+ endRcb = done_rcb->Eop;
+ do
+ {
+ curRcb->mode = CB_OWNERSHIP_BIT;
+ curRcb->Off_BLen = MAX_BUF_SIZE;
+ curRcb->Eop = 0;
+
+ pRxCtl->RxActQueueCount++;
+ num_bufs++;
+
+ lastRcb = curRcb;
+ curRcb = lastRcb->Next;
+
+ consistent_sync(lastRcb, sizeof(*lastRcb), DMA_TO_DEVICE);
+
+ } while (lastRcb != endRcb);
+
+ cppi_log_event1("[cppi] rx_return done", num_bufs);
+
+ // let the hardware know about the buffer(s)
+ tnetv_usb_reg_write(TNETV_USB_RX_FREE_BUF_CNT(ch), num_bufs);
+
+ return 0;
+}
+
+int tnetv_cppi_rx_int_recv(struct cppi_info *cppi, int ch, int *buf_size, void *buf, int maxpacket)
+{
+ cppi_rxcntl *pRxCtl = &cppi->rx_ctl[ch];
+ cppi_rcb *CurrentRcb, *LastRcb = 0, *SopRcb;
+ uint8_t *cur_buf_data_addr;
+ int cur_buf_bytes;
+ int copy_buf_size = *buf_size;
+ int ret = -EAGAIN;
+
+ *buf_size = 0;
+
+ CurrentRcb = pRxCtl->RxFakeRcvHead;
+ if (!CurrentRcb)
+ {
+ cppi_log_event2("[cppi] rx_int recv: nothing in q", tnetv_usb_reg_read(TNETV_USB_RX_INT_STATUS), tnetv_usb_reg_read(VL_INTST));
+ return -1;
+ }
+
+ cppi_log_event1("[cppi] rx_int recv (ch)", ch);
+ cppi_log_event4(" [cppi] recv - Processing SOP descriptor fb hd tl", (uint32_t) CurrentRcb, CurrentRcb->fake_bytes, (uint32_t) pRxCtl->RxFakeRcvHead, (uint32_t) pRxCtl->RxFakeRcvTail);
+
+ SopRcb = CurrentRcb;
+ LastRcb = 0;
+
+ do
+ {
+ // convert from vlynq phys to virt
+ cur_buf_data_addr = (uint8_t *) __vlynq_phys_to_dma(CurrentRcb->BufPtr);
+ cur_buf_data_addr = (uint8_t *) __phys_to_virt(cur_buf_data_addr);
+ cur_buf_bytes = (CurrentRcb->mode) & CB_SIZE_MASK;
+
+ // make sure we don't overflow the buffer.
+ if (cur_buf_bytes > copy_buf_size)
+ {
+ ret = 0;
+ break;
+ }
+
+ // BEN - packet can be ZLP
+ if (cur_buf_bytes)
+ {
+ consistent_sync(cur_buf_data_addr, MAX_BUF_SIZE, DMA_FROM_DEVICE);
+
+ memcpy((buf + *buf_size), cur_buf_data_addr, cur_buf_bytes);
+
+ copy_buf_size -= cur_buf_bytes;
+ *buf_size += cur_buf_bytes;
+ CurrentRcb->fake_bytes -= cur_buf_bytes;
+ }
+ else
+ {
+ CurrentRcb->fake_bytes = 0;
+ }
+
+ cppi_log_event4(" [cppi] bytes totrcvd amtleft fake", cur_buf_bytes, *buf_size, copy_buf_size, CurrentRcb->fake_bytes);
+
+ LastRcb = CurrentRcb;
+ CurrentRcb = LastRcb->Next;
+
+ // sync out fake bytes info
+ consistent_sync(LastRcb, sizeof(*LastRcb), DMA_TO_DEVICE);
+
+ // make sure each packet processed individually
+ if (cur_buf_bytes < maxpacket)
+ {
+ ret = 0;
+ break;
+ }
+
+ } while (LastRcb != pRxCtl->RxFakeRcvTail && CurrentRcb->fake_bytes && copy_buf_size > 0);
+
+ // make sure that the CurrentRcb isn't in the cache
+ consistent_sync(CurrentRcb, sizeof(*CurrentRcb), DMA_FROM_DEVICE);
+
+ if (copy_buf_size == 0)
+ {
+ ret = 0;
+ }
+
+ if (LastRcb)
+ {
+ SopRcb->Eop = LastRcb;
+
+ cppi_log_event3(" [cppi] rcv end", *buf_size, (uint32_t) CurrentRcb, (uint32_t) SopRcb->Eop);
+
+ if (LastRcb == pRxCtl->RxFakeRcvTail)
+ {
+ pRxCtl->RxFakeRcvHead = 0;
+ pRxCtl->RxFakeRcvTail = 0;
+ }
+ else
+ {
+ pRxCtl->RxFakeRcvHead = CurrentRcb;
+ }
+
+ cppi_log_event1(" [cppi] st rx return", ch);
+ cppi_log_event2(" rcv fake hd tl", (uint32_t) pRxCtl->RxFakeRcvHead, (uint32_t) pRxCtl->RxFakeRcvTail);
+
+ // all done, clean up the RCBs
+ tnetv_cppi_rx_return(cppi, ch, SopRcb);
+ }
+
+ return ret;
+}
+
+/*
+ * This function processes receive interrupts. It traverses the receive
+ * buffer queue, extracting the data and passing it to the upper layer software via
+ * osReceive(). It handles all error conditions and fragments without valid data by
+ * immediately returning the RCB's to the RCB pool.
+ */
+int tnetv_cppi_rx_int(struct cppi_info *cppi, int ch)
+{
+ cppi_rxcntl *pRxCtl = &cppi->rx_ctl[ch];
+ cppi_rcb *CurrentRcb, *LastRcb = 0, *SopRcb;
+ uint32_t RxBufStatus,PacketsServiced;
+ int TotalFrags;
+
+ cppi_log_event1("[cppi] rx_int (ch)", ch);
+
+ CurrentRcb = pRxCtl->RxActQueueHead;
+
+ if (!CurrentRcb)
+ {
+ cppi_log_event1("[cppi] rx_int no bufs!", (uint32_t) CurrentRcb);
+ return -1;
+ }
+
+ // make sure that all of the buffers get an invalidated cache
+ consistent_sync(pRxCtl->RcbStart, sizeof(cppi_rcb) * CPPI_RX_NUM_BUFS, DMA_FROM_DEVICE);
+
+ RxBufStatus = CurrentRcb->mode;
+ PacketsServiced = 0;
+
+ cppi_log_event4("[cppi] currentrcb, mode numleft fake", (uint32_t) CurrentRcb, CurrentRcb->mode, pRxCtl->RxActQueueCount, CurrentRcb->fake_bytes);
+ cppi_log_event4("[cppi]", ((uint32_t *) CurrentRcb)[0], ((uint32_t *) CurrentRcb)[1], ((uint32_t *) CurrentRcb)[2], ((uint32_t *) CurrentRcb)[3]);
+
+ while(((RxBufStatus & CB_OWNERSHIP_BIT) == 0) && (pRxCtl->RxActQueueCount > 0))
+ {
+ cppi_log_event2(" [cppi]Processing SOP descriptor st", (uint32_t) CurrentRcb, RxBufStatus);
+
+ SopRcb = CurrentRcb;
+
+ TotalFrags = 0;
+
+ do
+ {
+ TotalFrags++;
+ PacketsServiced++;
+
+ // Write the completion pointer
+ tnetv_usb_reg_write(TNETV_DMA_RX_CMPL(ch), __dma_to_vlynq_phys(CurrentRcb->dma_handle));
+
+ CurrentRcb->fake_bytes = (CurrentRcb->mode) & 0xFFFF;
+
+ // BEN - make sure this gets marked!
+ if (!CurrentRcb->fake_bytes || (CurrentRcb->mode & CB_ZLP_GARBAGE))
+ {
+ CurrentRcb->mode &= 0xFFFF0000;
+ CurrentRcb->fake_bytes = 0x10000;
+ }
+
+ cppi_log_event1(" fake_bytes:", CurrentRcb->fake_bytes);
+
+ RxBufStatus = CurrentRcb->mode;
+ LastRcb = CurrentRcb;
+ CurrentRcb = LastRcb->Next;
+
+ // sync the fake_bytes value back to mem
+ consistent_sync(LastRcb, sizeof(*LastRcb), DMA_TO_DEVICE);
+
+ } while (((CurrentRcb->mode & CB_OWNERSHIP_BIT) == 0) && ((RxBufStatus & CB_EOF_BIT) == 0));
+
+ SopRcb->Eop = LastRcb;
+
+ pRxCtl->RxActQueueHead = CurrentRcb;
+ pRxCtl->RxActQueueCount -= TotalFrags;
+
+ if (LastRcb->mode & CB_EOQ_BIT)
+ {
+ if (CurrentRcb)
+ {
+ cppi_log_event1(" [cppi] rcv done q next", LastRcb->HNext);
+ tnetv_usb_reg_write(TNETV_DMA_RX_STATE(ch, TNETV_CPPI_RX_WORD_HDP), LastRcb->HNext);
+ }
+ else
+ {
+ cppi_log_event0(" [cppi] rcv done");
+
+ pRxCtl->RxActive = 0;
+ }
+ }
+
+ // BEN - add to the list of buffers we need to deal with
+ if (!pRxCtl->RxFakeRcvHead)
+ {
+ pRxCtl->RxFakeRcvHead = SopRcb;
+ pRxCtl->RxFakeRcvTail = SopRcb->Eop;
+ }
+ else
+ {
+ pRxCtl->RxFakeRcvTail = SopRcb->Eop;
+ }
+
+ // make sure we have enough buffers
+ cppi_log_event1(" nextrcb", CurrentRcb->mode);
+
+ if (CurrentRcb)
+ {
+ // continue the loop
+ RxBufStatus = CurrentRcb->mode;
+ }
+
+ } /* while */
+
+ cppi_log_event2("[cppi] fake hd tl", (uint32_t) pRxCtl->RxFakeRcvHead, (uint32_t) pRxCtl->RxFakeRcvTail);
+
+ // sync out all buffers before leaving
+ consistent_sync(pRxCtl->RcbStart, (CPPI_RX_NUM_BUFS * sizeof(cppi_rcb)), DMA_FROM_DEVICE);
+
+ return PacketsServiced;
+}
+
+static void tnetv_cppi_rx_queue_init(struct cppi_info *cppi, int ch, dma_addr_t buf, unsigned length)
+{
+ cppi_rxcntl *pRxCtl = &cppi->rx_ctl[ch];
+ cppi_rcb *rcb, *first_rcb;
+ unsigned int queued_len = 0;
+ int rcblen;
+ int num_frags = 0;
+ dma_addr_t buf_ptr;
+
+ if (length == 0)
+ {
+ cppi_log_event0("[cppi] len = 0, nothing to recv");
+ return;
+ }
+
+ // usb_requests can have a 32 bit length, but CPPI DMA fragments
+ // have a 64k limit. Split the usb_request up into fragments here.
+ first_rcb = pRxCtl->RcbPool;
+ rcb = first_rcb;
+
+ cppi_log_event2("[cppi] Rx queue add: head len", (uint32_t) first_rcb, length);
+
+ while (queued_len < length)
+ {
+ buf_ptr = buf + queued_len;
+ rcb->BufPtr = __dma_to_vlynq_phys(buf_ptr);
+
+ rcb->HNext = 0;
+ rcb->mode = CB_OWNERSHIP_BIT;
+
+ rcblen = ((length - queued_len) > MAX_BUF_SIZE) ? MAX_BUF_SIZE : (length - queued_len);
+ rcb->Off_BLen = rcblen;
+
+ queued_len += rcblen;
+ if (queued_len < length)
+ {
+ rcb->HNext = __dma_to_vlynq_phys(((cppi_rcb *) (rcb->Next))->dma_handle);
+ rcb = rcb->Next;
+ }
+
+ num_frags++;
+ }
+
+ pRxCtl->RcbPool = rcb->Next;
+ rcb->Next = 0;
+
+ cppi_log_event4("[cppi] Adding Rcb (dma) (paddr) (buf)", (uint32_t) rcb, rcb->dma_handle, __dma_to_vlynq_phys(rcb->dma_handle), (uint32_t) rcb->BufPtr);
+ cppi_log_event4("[cppi] Next HNext (len) of (total)", (uint32_t) rcb->Next, rcb->HNext, queued_len, length);
+
+ pRxCtl->RxActQueueCount += num_frags;
+
+ cppi_log_event4("[cppi] rx queued (ptr) (len) (ftcb, ltcb)", (uint32_t) rcb->BufPtr, rcb->Off_BLen, (uint32_t) first_rcb, (uint32_t) rcb);
+ cppi_log_event2(" [cppi] mode num_frags", rcb->mode, num_frags);
+
+ pRxCtl->RxActQueueHead = first_rcb;
+ pRxCtl->RxActQueueTail = rcb;
+
+ cppi_log_event2("[cppi] setting rx (head) (virt)", (uint32_t) first_rcb, __dma_to_vlynq_phys(first_rcb->dma_handle));
+ cppi_log_event4("[cppi] ", ((uint32_t *) first_rcb)[0], ((uint32_t *) first_rcb)[1], ((uint32_t *) first_rcb)[2], ((uint32_t *) first_rcb)[3]);
+
+ // make this into a circular buffer so we never get caught with
+ // no free buffers left
+ rcb->Next = pRxCtl->RxActQueueHead;
+ rcb->HNext = (uint32_t) (__dma_to_vlynq_phys(pRxCtl->RxActQueueHead->dma_handle));
+}
+
+int tnetv_cppi_rx_queue_add(struct cppi_info *cppi, int ch, dma_addr_t buf, unsigned length)
+{
+ (void)buf;
+ (void)length;
+ cppi_rxcntl *pRxCtl = &cppi->rx_ctl[ch];
+ unsigned int cur_bufs;
+
+ cur_bufs = tnetv_usb_reg_read(TNETV_USB_RX_FREE_BUF_CNT(ch));
+
+ if (!pRxCtl->RxActive)
+ {
+ cppi_log_event0("[cppi] queue add - not active");
+
+ pRxCtl->RcbPool = (cppi_rcb *) pRxCtl->RcbStart;
+
+ // add all the buffers to the active (circular) queue
+ tnetv_cppi_rx_queue_init(cppi, ch, (dma_addr_t) __virt_to_phys(dma_recv_buf[ch]), (MAX_BUF_SIZE * pRxCtl->RxNumBuffers));
+
+ /* write Rx Queue Head Descriptor Pointer */
+ tnetv_usb_reg_write(TNETV_DMA_RX_STATE(ch, TNETV_CPPI_RX_WORD_HDP), __dma_to_vlynq_phys(pRxCtl->RxActQueueHead->dma_handle));
+
+ pRxCtl->RxActive = 1;
+
+ // sync out all buffers before starting
+ consistent_sync(pRxCtl->RcbStart, (CPPI_RX_NUM_BUFS * sizeof(cppi_rcb)), DMA_TO_DEVICE);
+
+ // sync out temp rx buffer
+ consistent_sync(dma_recv_buf[ch], CPPI_DMA_RX_BUF_SIZE, DMA_FROM_DEVICE);
+
+ if (cur_bufs < pRxCtl->RxActQueueCount)
+ {
+ // let the hardware know about the buffer(s)
+ tnetv_usb_reg_write(TNETV_USB_RX_FREE_BUF_CNT(ch), pRxCtl->RxActQueueCount - cur_bufs);
+ }
+ }
+
+ cppi_log_event3("[cppi] rx add: (cur_bufs) (avail_bufs) (now)", cur_bufs, pRxCtl->RxActQueueCount, tnetv_usb_reg_read(TNETV_USB_RX_FREE_BUF_CNT(ch)));
+
+ return 0;
+}
+
+int tnetv_cppi_flush_rx_queue(struct cppi_info *cppi, int ch)
+{
+ cppi_rxcntl *pRxCtl = &cppi->rx_ctl[ch];
+ cppi_rcb *rcb;
+ int num_bufs;
+
+ cppi_log_event1("[cppi] flush RX ", (uint32_t) pRxCtl->RxActQueueHead);
+
+ // flush out any pending receives
+ tnetv_cppi_rx_int(cppi, ch);
+
+ // now discard all received data
+ rcb = pRxCtl->RxFakeRcvHead;
+
+ if (rcb)
+ {
+ rcb->Eop = pRxCtl->RxFakeRcvTail;
+
+ // clean up any unreceived RCBs
+ tnetv_cppi_rx_return(cppi, ch, rcb);
+ }
+
+ pRxCtl->RxFakeRcvHead = 0;
+ pRxCtl->RxFakeRcvTail = 0;
+
+ pRxCtl->RxActive = 0;
+
+ // drain the HW free buffer count
+ num_bufs = tnetv_usb_reg_read(TNETV_USB_RX_FREE_BUF_CNT(ch));
+ tnetv_usb_reg_write(TNETV_USB_RX_FREE_BUF_CNT(ch), -num_bufs);
+
+ cppi_log_event2("[cppi] flush RX queue done (freed) act: ", num_bufs, (uint32_t) pRxCtl->RxActQueueCount);
+
+ return 0;
+}
+
+
+/*
+ * This function allocates receive buffer descriptors (internal CPHAL function).
+ * After allocation, the function 'queues' (gives to the hardware) the newly
+ * created receive buffers to enable packet reception.
+ *
+ * @param ch Channel number.
+ *
+ * @return 0 OK, Non-Zero Not OK
+ */
+int tnetv_cppi_init_rcb(struct cppi_info *cppi, int ch)
+{
+ int i, num;
+ cppi_rcb *pRcb;
+ char *AllRcb;
+ int rcbSize;
+ cppi_rxcntl *pRxCtl = &cppi->rx_ctl[ch];
+
+ num = pRxCtl->RxNumBuffers;
+ rcbSize = (sizeof(cppi_rcb) + 0xf) & ~0xf;
+
+ cppi_log_event2("[cppi] init_rcb ch num", ch, num);
+
+ if (pRxCtl->RxNumBuffers == 0)
+ {
+ return -1;
+ }
+
+ /* if the memory has already been allocated, simply reuse it! */
+ AllRcb = pRxCtl->RcbStart;
+
+ // now reinitialize the RCB pool
+ pRxCtl->RcbPool = 0;
+ for (i = (num - 1); i >= 0; i--)
+ {
+ pRcb = (cppi_rcb *)(AllRcb + (i * rcbSize));
+
+ pRcb->dma_handle = pRxCtl->rcb_start_dma_addr + (i * rcbSize);
+
+ pRcb->BufPtr = 0;
+ pRcb->mode = 0;
+ pRcb->HNext = 0;
+ pRcb->Next = (void *) pRxCtl->RcbPool;
+ pRcb->Off_BLen = 0;
+ pRcb->Eop = 0;
+ pRcb->fake_bytes = 0;
+
+ pRxCtl->RcbPool = pRcb;
+ }
+
+ cppi_log_event2(" [cppi]RcbPool (dma)", (uint32_t) pRxCtl->RcbPool, pRxCtl->RcbPool->dma_handle);
+
+ pRxCtl->RxActQueueCount = 0;
+ pRxCtl->RxActQueueHead = 0;
+ pRxCtl->RxActive = 0;
+
+ pRxCtl->RxFakeRcvHead = 0;
+ pRxCtl->RxFakeRcvTail = 0;
+
+ return 0;
+}
+
+static uint8_t ch_buf_cnt[][2] = {
+ {CPPI_RX_NUM_BUFS, 2}, // ch0: bulk out/in
+ {CPPI_RX_NUM_BUFS, 2}, // ch1: bulk out/in
+ {0, 2}, // ch2: interrupt
+ {0, 2} // ch3: interrupt
+};
+
+void tnetv_cppi_init(struct cppi_info *cppi)
+{
+ int ch;
+ uint8_t *alloc_ptr;
+ int ch_mem_size[CPPI_NUM_CHANNELS];
+
+ // wipe cppi memory
+ memset(cppi, 0, sizeof(*cppi));
+
+ // find out how much memory we need to allocate
+ cppi->dma_size = 0;
+ for (ch = 0; ch < CPPI_NUM_CHANNELS; ch++)
+ {
+ ch_mem_size[ch] = (ch_buf_cnt[ch][0] * sizeof(cppi_rcb)) + (ch_buf_cnt[ch][1] * sizeof(cppi_tcb));
+ cppi->dma_size += ch_mem_size[ch];
+ }
+
+ // allocate DMA-able memory
+ if (cppi->dma_size != CPPI_INFO_MEM_SIZE)
+ {
+ panicf("Invalid dma size expected %d got %d", cppi->dma_size, CPPI_INFO_MEM_SIZE);
+ }
+ cppi->dma_handle = (dma_addr_t) __virt_to_phys(cppi->dma_mem);
+
+ memset(cppi->dma_mem, 0, cppi->dma_size);
+
+ cppi_log_event2("[cppi] all CBs sz mem", cppi->dma_size, (uint32_t) cppi->dma_mem);
+
+ // now set up the pointers
+ alloc_ptr = cppi->dma_mem;
+ for (ch = 0; ch < CPPI_NUM_CHANNELS; ch++)
+ {
+ cppi->rx_ctl[ch].RxNumBuffers = ch_buf_cnt[ch][0];
+ cppi->rx_ctl[ch].RcbStart = alloc_ptr;
+ cppi->rx_ctl[ch].rcb_start_dma_addr = (dma_addr_t) __virt_to_phys(alloc_ptr);
+ alloc_ptr += (ch_buf_cnt[ch][0] * sizeof(cppi_rcb));
+
+ cppi->tx_ctl[ch].TxNumBuffers = ch_buf_cnt[ch][1];
+ cppi->tx_ctl[ch].TcbStart = alloc_ptr;
+ cppi->tx_ctl[ch].tcb_start_dma_addr = (dma_addr_t) __virt_to_phys(alloc_ptr);
+ alloc_ptr += (ch_buf_cnt[ch][1] * sizeof(cppi_tcb));
+
+ cppi_log_event3("[cppi] alloc bufs: ch dmarcb dmatcb", ch, cppi->rx_ctl[ch].rcb_start_dma_addr, cppi->tx_ctl[ch].tcb_start_dma_addr);
+
+ // set up receive buffer
+ if (ch_buf_cnt[ch][0])
+ {
+ dma_recv_buf[ch] = (ch == 0) ? ch0_rx_buf : ((ch == 1) ? ch1_rx_buf : 0);
+ cppi_log_event3("[cppi] Alloc fake DMA buf ch", ch, (uint32_t) dma_recv_buf[ch], (uint32_t) __virt_to_phys(dma_recv_buf[ch]));
+ }
+ else
+ {
+ dma_recv_buf[ch] = 0;
+ }
+ }
+
+}
+
+void tnetv_cppi_cleanup(struct cppi_info *cppi)
+{
+ cppi_log_event0("wipe cppi mem");
+
+ // wipe cppi memory
+ memset(cppi, 0, sizeof(*cppi));
+}
diff --git a/firmware/target/arm/tms320dm320/sansa-connect/tnetv105_cppi.h b/firmware/target/arm/tms320dm320/sansa-connect/tnetv105_cppi.h
new file mode 100644
index 0000000000..b240f9593d
--- /dev/null
+++ b/firmware/target/arm/tms320dm320/sansa-connect/tnetv105_cppi.h
@@ -0,0 +1,144 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id: $
+ *
+ * Copyright (C) 2021 by Tomasz Moń
+ * Copied with minor modifications from Sansa Connect Linux driver
+ * Copyright (c) 2005 Zermatt Systems, Inc.
+ * Written by: Ben Bostwick
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef TNETV105_CPPI_H
+#define TNETV105_CPPI_H
+
+#include <stdint.h>
+#include "errno.h"
+
+typedef uint32_t dma_addr_t;
+#define USB_CPPI_LOGGING 0
+#define CPPI_RX_NUM_BUFS 2
+#define CPPI_INFO_MEM_SIZE (2 * CPPI_RX_NUM_BUFS * sizeof(cppi_rcb) + 4 * 2 * sizeof(cppi_tcb))
+
+#define CPPI_NUM_CHANNELS 4
+#define CPPI_MAX_FRAG 0xFE00
+
+struct cppi_info;
+
+typedef struct
+{
+ uint32_t HNext; /*< Hardware's pointer to next buffer descriptor */
+ uint32_t BufPtr; /*< Pointer to the data buffer */
+ uint32_t Off_BLen; /*< Contains buffer offset and buffer length */
+ uint32_t mode; /*< SOP, EOP, Ownership, EOQ, Teardown, Q Starv, Length */
+ void *Next;
+ void *Eop;
+ dma_addr_t dma_handle;
+ uint32_t dummy;
+
+} cppi_tcb;
+
+typedef struct
+{
+ uint32_t HNext; /*< Hardware's pointer to next buffer descriptor */
+ uint32_t BufPtr; /*< Pointer to the data buffer */
+ uint32_t Off_BLen; /*< Contains buffer offset and buffer length */
+ uint32_t mode; /*< SOP, EOP, Ownership, EOQ, Teardown Complete bits */
+ void *Next;
+ void *Eop;
+ dma_addr_t dma_handle;
+ uint32_t fake_bytes;
+
+} cppi_rcb;
+
+typedef struct cppi_txcntl
+{
+ cppi_tcb *TcbPool;
+ cppi_tcb *TxActQueueHead;
+ cppi_tcb *TxActQueueTail;
+ uint32_t TxActQueueCount;
+ uint32_t TxActive;
+ cppi_tcb *LastTcbProcessed;
+ char *TcbStart;
+ dma_addr_t tcb_start_dma_addr;
+ int TxNumBuffers;
+
+#ifdef _CPHAL_STATS
+ uint32_t TxMisQCnt;
+ uint32_t TxEOQCnt;
+ uint32_t TxPacketsServiced;
+ uint32_t TxMaxServiced;
+ uint32_t NumTxInt;
+#endif
+} cppi_txcntl;
+
+
+typedef struct cppi_rxcntl
+{
+ cppi_rcb *RcbPool;
+ cppi_rcb *RxActQueueHead;
+ cppi_rcb *RxActQueueTail;
+ uint32_t RxActQueueCount;
+ uint32_t RxActive;
+ char *RcbStart;
+ dma_addr_t rcb_start_dma_addr;
+ int RxNumBuffers;
+
+ cppi_rcb *RxFakeRcvHead;
+ cppi_rcb *RxFakeRcvTail;
+
+#ifdef _CPHAL_STATS
+ uint32_t RxMisQCnt;
+ uint32_t RxEOQCnt;
+ uint32_t RxMaxServiced;
+ uint32_t RxPacketsServiced;
+ uint32_t NumRxInt;
+#endif
+} cppi_rxcntl;
+
+typedef struct cppi_info
+{
+ struct cppi_txcntl tx_ctl[CPPI_NUM_CHANNELS];
+ struct cppi_rxcntl rx_ctl[CPPI_NUM_CHANNELS];
+
+ uint8_t dma_mem[CPPI_INFO_MEM_SIZE];
+ int dma_size;
+ dma_addr_t dma_handle;
+
+} cppi_info;
+
+#define tnetv_cppi_rx_int_recv_check(cppi, ch) (((cppi)->rx_ctl[(ch)].RxFakeRcvHead) ? 1 : 0)
+
+int tnetv_cppi_init_tcb(struct cppi_info *cppi, int ch);
+int tnetv_cppi_flush_tx_queue(struct cppi_info *cppi, int ch);
+int tnetv_cppi_send(struct cppi_info *cppi, int ch, dma_addr_t buf, unsigned length, int send_zlp);
+int tnetv_cppi_tx_int(struct cppi_info *cppi, int ch);
+void tnetv_cppi_free_tcb(struct cppi_info *cppi, int ch);
+
+int tnetv_cppi_init_rcb(struct cppi_info *cppi, int ch);
+int tnetv_cppi_flush_rx_queue(struct cppi_info *cppi, int ch);
+int tnetv_cppi_rx_return(struct cppi_info *cppi, int ch, cppi_rcb *done_rcb);
+int tnetv_cppi_rx_queue_add(struct cppi_info *cppi, int ch, dma_addr_t buf, unsigned length);
+int tnetv_cppi_rx_int(struct cppi_info *cppi, int ch);
+int tnetv_cppi_rx_int_recv(struct cppi_info *cppi, int ch, int *buf_size, void *buf, int maxpacket);
+void tnetv_cppi_free_rcb(struct cppi_info *cppi, int ch);
+
+void tnetv_cppi_init(struct cppi_info *cppi);
+void tnetv_cppi_cleanup(struct cppi_info *cppi);
+
+void tnetv_cppi_dump_info(struct cppi_info *cppi);
+
+#endif
diff --git a/firmware/target/arm/tms320dm320/sansa-connect/tnetv105_usb_drv.c b/firmware/target/arm/tms320dm320/sansa-connect/tnetv105_usb_drv.c
new file mode 100644
index 0000000000..e5a9000f40
--- /dev/null
+++ b/firmware/target/arm/tms320dm320/sansa-connect/tnetv105_usb_drv.c
@@ -0,0 +1,1519 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id: $
+ *
+ * Copyright (C) 2021 by Tomasz Moń
+ * Ported from Sansa Connect TNETV105 UDC Linux driver
+ * Copyright (c) 2005,2006 Zermatt Systems, Inc.
+ * Written by: Ben Bostwick
+ * Linux driver was modeled strongly after the pxa usb driver.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "config.h"
+#include "system.h"
+#include "kernel.h"
+#include "panic.h"
+#include "logf.h"
+#include "usb.h"
+#include "usb_drv.h"
+#include "usb_core.h"
+#include <string.h>
+#include "tnetv105_usb_drv.h"
+#include "tnetv105_cppi.h"
+
+#ifdef SANSA_CONNECT
+#define SDRAM_SIZE 0x04000000
+
+static void set_tnetv_reset(bool high)
+{
+ if (high)
+ {
+ IO_GIO_BITSET0 = (1 << 7);
+ }
+ else
+ {
+ IO_GIO_BITCLR0 = (1 << 7);
+ }
+}
+
+static bool is_tnetv_reset_high(void)
+{
+ return (IO_GIO_BITSET0 & (1 << 7)) ? true : false;
+}
+#endif
+
+static bool setup_is_set_address;
+
+static cppi_info cppi;
+
+static struct ep_runtime_t
+{
+ int max_packet_size;
+ bool in_allocated;
+ bool out_allocated;
+ uint8_t *rx_buf; /* OUT */
+ int rx_remaining;
+ int rx_size;
+ uint8_t *tx_buf; /* IN */
+ int tx_remaining;
+ int tx_size;
+ volatile bool block; /* flag indicating that transfer is blocking */
+ struct semaphore complete; /* semaphore for blocking transfers */
+}
+ep_runtime[USB_NUM_ENDPOINTS];
+
+static const struct
+{
+ int type;
+ int hs_max_packet_size;
+ /* Not sure what xyoff[1] is for. Presumably it is double buffer, but how
+ * the double buffering works is not so clear from the Sansa Connect Linux
+ * kernel patch. As TNETV105 datasheet is not available, the values are
+ * simply taken from the Linux patch as potential constraints are unknown.
+ *
+ * Linux kernel has 9 endpoints:
+ * * 0: ep0
+ * * 1: ep1in-bulk
+ * * 2: ep2out-bulk
+ * * 3: ep3in-int
+ * * 4: ep4in-int
+ * * 5: ep1out-bulk
+ * * 6: ep2in-bulk
+ * * 7: ep3out-int
+ * * 8: ep4out-int
+ */
+ uint16_t xyoff_in[2];
+ uint16_t xyoff_out[2];
+}
+ep_const_data[USB_NUM_ENDPOINTS] =
+{
+ {
+ .type = USB_ENDPOINT_XFER_CONTROL,
+ .hs_max_packet_size = EP0_MAX_PACKET_SIZE,
+ /* Do not set xyoff as it likely does not apply here.
+ * Linux simply hardcodes the offsets when needed.
+ */
+ },
+ {
+ .type = USB_ENDPOINT_XFER_BULK,
+ .hs_max_packet_size = EP1_MAX_PACKET_SIZE,
+ .xyoff_in = {EP1_XBUFFER_ADDRESS, EP1_YBUFFER_ADDRESS},
+ .xyoff_out = {EP5_XBUFFER_ADDRESS, EP5_YBUFFER_ADDRESS},
+ },
+ {
+ .type = USB_ENDPOINT_XFER_BULK,
+ .hs_max_packet_size = EP2_MAX_PACKET_SIZE,
+ .xyoff_in = {EP6_XBUFFER_ADDRESS, EP6_YBUFFER_ADDRESS},
+ .xyoff_out = {EP2_XBUFFER_ADDRESS, EP2_YBUFFER_ADDRESS},
+ },
+ {
+ .type = USB_ENDPOINT_XFER_INT,
+ .hs_max_packet_size = EP3_MAX_PACKET_SIZE,
+ .xyoff_in = {EP3_XBUFFER_ADDRESS, EP3_YBUFFER_ADDRESS},
+ .xyoff_out = {EP7_XBUFFER_ADDRESS, EP7_YBUFFER_ADDRESS},
+ },
+ {
+ .type = USB_ENDPOINT_XFER_INT,
+ .hs_max_packet_size = EP4_MAX_PACKET_SIZE,
+ .xyoff_in = {EP4_XBUFFER_ADDRESS, EP4_YBUFFER_ADDRESS},
+ .xyoff_out = {EP8_XBUFFER_ADDRESS, EP8_YBUFFER_ADDRESS},
+ },
+};
+
+#define VLYNQ_CTL_RESET_MASK 0x0001
+#define VLYNQ_CTL_CLKDIR_MASK 0x8000
+#define VLYNQ_STS_LINK_MASK 0x0001
+
+#define DM320_VLYNQ_CTRL_RESET (1 << 0)
+#define DM320_VLYNQ_CTRL_LOOP (1 << 1)
+#define DM320_VLYNQ_CTRL_ADR_OPT (1 << 2)
+#define DM320_VLYNQ_CTRL_INT_CFG (1 << 7)
+#define DM320_VLYNQ_CTRL_INT_VEC_MASK (0x00001F00)
+#define DM320_VLYNQ_CTRL_INT_EN (1 << 13)
+#define DM320_VLYNQ_CTRL_INT_LOC (1 << 14)
+#define DM320_VLYNQ_CTRL_CLKDIR (1 << 15)
+#define DM320_VLYNQ_CTRL_CLKDIV_MASK (0x00070000)
+#define DM320_VLYNQ_CTRL_PWR_MAN (1 << 31)
+
+#define DM320_VLYNQ_STAT_LINK (1 << 0)
+#define DM320_VLYNQ_STAT_MST_PEND (1 << 1)
+#define DM320_VLYNQ_STAT_SLV_PEND (1 << 2)
+#define DM320_VLYNQ_STAT_F0_NE (1 << 3)
+#define DM320_VLYNQ_STAT_F1_NE (1 << 4)
+#define DM320_VLYNQ_STAT_F2_NE (1 << 5)
+#define DM320_VLYNQ_STAT_F3_NE (1 << 6)
+#define DM320_VLYNQ_STAT_LOC_ERR (1 << 7)
+#define DM320_VLYNQ_STAT_REM_ERR (1 << 8)
+#define DM320_VLYNQ_STAT_FC_OUT (1 << 9)
+#define DM320_VLYNQ_STAT_FC_IN (1 << 10)
+
+#define MAX_PACKET(epn, speed) ((((speed) == USB_SPEED_HIGH) && (((epn) == 1) || ((epn) == 2))) ? USB_HIGH_SPEED_MAXPACKET : USB_FULL_SPEED_MAXPACKET)
+
+#define VLYNQ_INTR_USB20 (1 << 0)
+#define VLYNQ_INTR_CPPI (1 << 1)
+
+static inline void set_vlynq_clock(bool enable)
+{
+ if (enable)
+ {
+ IO_CLK_MOD2 |= (1 << 13);
+ }
+ else
+ {
+ IO_CLK_MOD2 &= ~(1 << 13);
+ }
+}
+
+static inline void set_vlynq_irq(bool enabled)
+{
+ if (enabled)
+ {
+ /* Enable VLYNQ interrupt */
+ IO_INTC_EINT1 |= (1 << 0);
+ }
+ else
+ {
+ IO_INTC_EINT1 &= ~(1 << 0);
+ }
+}
+
+static int tnetv_hw_reset(void)
+{
+ int timeout;
+
+ /* hold down the reset pin on the USB chip */
+ set_tnetv_reset(false);
+
+ /* Turn on VLYNQ clock. */
+ set_vlynq_clock(true);
+
+ /* now reset the VLYNQ module */
+ VL_CTRL |= (VLYNQ_CTL_CLKDIR_MASK | DM320_VLYNQ_CTRL_PWR_MAN);
+ VL_CTRL |= VLYNQ_CTL_RESET_MASK;
+
+ mdelay(10);
+
+ /* pull up the reset pin */
+ set_tnetv_reset(true);
+
+ /* take the VLYNQ out of reset */
+ VL_CTRL &= ~VLYNQ_CTL_RESET_MASK;
+
+ timeout = 0;
+ while (!(VL_STAT & VLYNQ_STS_LINK_MASK) && timeout++ < 50);
+ {
+ mdelay(40);
+ }
+
+ if (!(VL_STAT & VLYNQ_STS_LINK_MASK))
+ {
+ logf("ERROR: VLYNQ not initialized!\n");
+ return -1;
+ }
+
+ /* set up vlynq local map */
+ VL_TXMAP = DM320_VLYNQ_PADDR;
+ VL_RXMAPOF1 = CONFIG_SDRAM_START;
+ VL_RXMAPSZ1 = SDRAM_SIZE;
+
+ /* set up vlynq remote map for tnetv105 */
+ VL_TXMAP_R = 0x00000000;
+ VL_RXMAPOF1_R = 0x0C000000;
+ VL_RXMAPSZ1_R = 0x00030000;
+
+ /* clear TNETV gpio state */
+ tnetv_usb_reg_write(TNETV_V2USB_GPIO_FS, 0);
+
+ /* set USB_CHARGE_EN pin (gpio 1) - output, disable pullup */
+ tnetv_usb_reg_write(TNETV_V2USB_GPIO_DOUT, 0);
+ tnetv_usb_reg_write(TNETV_V2USB_GPIO_DIR, 0xffff);
+
+ return 0;
+}
+
+static int tnetv_xcvr_on(void)
+{
+ return tnetv_hw_reset();
+}
+
+static void tnetv_xcvr_off(void)
+{
+ /* turn off vlynq module clock */
+ set_vlynq_clock(false);
+
+ /* hold down the reset pin on the USB chip */
+ set_tnetv_reset(false);
+}
+
+/* Copy data from the usb data memory. The memory reads should be done 32 bits at a time.
+ * We do not assume that the dst data is aligned.
+ */
+static void tnetv_copy_from_data_mem(void *dst, const volatile uint32_t *sp, int size)
+{
+ uint8_t *dp = (uint8_t *) dst;
+ uint32_t value;
+
+ while (size >= 4)
+ {
+ value = *sp++;
+ dp[0] = value;
+ dp[1] = value >> 8;
+ dp[2] = value >> 16;
+ dp[3] = value >> 24;
+ dp += 4;
+ size -= 4;
+ }
+
+ if (size)
+ {
+ value = sp[0];
+ switch (size)
+ {
+ case 3:
+ dp[2] = value >> 16;
+ case 2:
+ dp[1] = value >> 8;
+ case 1:
+ dp[0] = value;
+ break;
+ }
+ }
+}
+
+/* Copy data into the usb data memory. The memory writes must be done 32 bits at a time.
+ * We do not assume that the src data is aligned.
+*/
+static void tnetv_copy_to_data_mem(volatile uint32_t *dp, const void *src, int size)
+{
+ const uint8_t *sp = (const uint8_t *) src;
+ uint32_t value;
+
+ while (size >= 4)
+ {
+ value = sp[0] | (sp[1] << 8) | (sp[2] << 16) | (sp[3] << 24);
+ *dp++ = value;
+ sp += 4;
+ size -= 4;
+ }
+
+ switch (size)
+ {
+ case 3:
+ value = sp[0] | (sp[1] << 8) | (sp[2] << 16);
+ *dp = value;
+ break;
+ case 2:
+ value = sp[0] | (sp[1] << 8);
+ *dp = value;
+ break;
+ case 1:
+ value = sp[0];
+ *dp = value;
+ break;
+ }
+}
+
+static void tnetv_init_endpoints(void)
+{
+ UsbEp0CtrlType ep0Cfg;
+ UsbEp0ByteCntType ep0Cnt;
+ UsbEpCfgCtrlType epCfg;
+ UsbEpStartAddrType epStartAddr;
+ int ch, wd, epn;
+
+ ep0Cnt.val = 0;
+ ep0Cnt.f.out_ybuf_nak = 1;
+ ep0Cnt.f.out_xbuf_nak = 1;
+ ep0Cnt.f.in_ybuf_nak = 1;
+ ep0Cnt.f.in_xbuf_nak = 1;
+ tnetv_usb_reg_write(TNETV_USB_EP0_CNT, ep0Cnt.val);
+
+ /* Setup endpoint zero */
+ ep0Cfg.val = 0;
+ ep0Cfg.f.buf_size = EP0_BUF_SIZE_64; /* must be 64 bytes for USB 2.0 */
+ ep0Cfg.f.dbl_buf = 0;
+ ep0Cfg.f.in_en = 1;
+ ep0Cfg.f.in_int_en = 1;
+ ep0Cfg.f.out_en = 1;
+ ep0Cfg.f.out_int_en = 1;
+ tnetv_usb_reg_write(TNETV_USB_EP0_CFG, ep0Cfg.val);
+
+ /* disable cell dma */
+ tnetv_usb_reg_write(TNETV_USB_CELL_DMA_EN, 0);
+
+ /* turn off dma engines */
+ tnetv_usb_reg_write(TNETV_USB_TX_CTL, 0);
+ tnetv_usb_reg_write(TNETV_USB_RX_CTL, 0);
+
+ /* clear out DMA registers */
+ for (ch = 0; ch < TNETV_DMA_NUM_CHANNELS; ch++)
+ {
+ for (wd = 0; wd < TNETV_DMA_TX_NUM_WORDS; wd++)
+ {
+ tnetv_usb_reg_write(TNETV_DMA_TX_STATE(ch, wd), 0);
+ }
+
+ for (wd = 0; wd < TNETV_DMA_RX_NUM_WORDS; wd++)
+ {
+ tnetv_usb_reg_write(TNETV_DMA_RX_STATE(ch, wd), 0);
+ }
+
+ /* flush the free buf count */
+ while (tnetv_usb_reg_read(TNETV_USB_RX_FREE_BUF_CNT(ch)) != 0)
+ {
+ tnetv_usb_reg_write(TNETV_USB_RX_FREE_BUF_CNT(ch), 0xFFFF);
+ }
+ }
+
+ for (epn = 1; epn < USB_NUM_ENDPOINTS; epn++)
+ {
+ tnetv_usb_reg_write(TNETV_USB_EPx_ADR(epn),0);
+ tnetv_usb_reg_write(TNETV_USB_EPx_CFG(epn), 0);
+ tnetv_usb_reg_write(TNETV_USB_EPx_IN_CNT(epn), 0x80008000);
+ tnetv_usb_reg_write(TNETV_USB_EPx_OUT_CNT(epn), 0x80008000);
+ }
+
+ /* Setup the other endpoints */
+ for (epn = 1; epn < USB_NUM_ENDPOINTS; epn++)
+ {
+ epCfg.val = tnetv_usb_reg_read(TNETV_USB_EPx_CFG(epn));
+ epStartAddr.val = tnetv_usb_reg_read(TNETV_USB_EPx_ADR(epn));
+
+ /* Linux kernel enables dbl buf for both IN and OUT.
+ * For IN this is problematic when tnetv_cppi_send() is called
+ * to send single ZLP, it will actually send two ZLPs.
+ * Disable the dbl buf here as datasheet is not available and
+ * this results in working mass storage on Windows 10.
+ */
+ epCfg.f.in_dbl_buf = 0;
+ epCfg.f.in_toggle_rst = 1;
+ epCfg.f.in_ack_int = 0;
+ epCfg.f.in_stall = 0;
+ epCfg.f.in_nak_int = 0;
+ epCfg.f.out_dbl_buf = 0;
+ epCfg.f.out_toggle_rst = 1;
+ epCfg.f.out_ack_int = 0;
+ epCfg.f.out_stall = 0;
+ epCfg.f.out_nak_int = 0;
+
+ /* buf_size is specified "in increments of 8 bytes" */
+ epCfg.f.in_buf_size = ep_const_data[epn].hs_max_packet_size >> 3;
+ epCfg.f.out_buf_size = ep_const_data[epn].hs_max_packet_size >> 3;
+
+ epStartAddr.f.xBuffStartAddrIn = ep_const_data[epn].xyoff_in[0] >> 4;
+ epStartAddr.f.yBuffStartAddrIn = ep_const_data[epn].xyoff_in[1] >> 4;
+ epStartAddr.f.xBuffStartAddrOut = ep_const_data[epn].xyoff_out[0] >> 4;
+ epStartAddr.f.yBuffStartAddrOut = ep_const_data[epn].xyoff_out[1] >> 4;
+
+ /* allocate memory for DMA */
+ tnetv_cppi_init_rcb(&cppi, (epn - 1));
+ /* set up DMA queue */
+ tnetv_cppi_init_tcb(&cppi, (epn - 1));
+
+ /* now write out the config to the TNETV (write enable bits last) */
+ tnetv_usb_reg_write(TNETV_USB_EPx_ADR(epn), epStartAddr.val);
+ tnetv_usb_reg_write(TNETV_USB_EPx_CFG(epn), epCfg.val);
+ tnetv_usb_reg_write(TNETV_USB_EPx_IN_CNT(epn), 0x80008000);
+ tnetv_usb_reg_write(TNETV_USB_EPx_OUT_CNT(epn), 0x80008000);
+ }
+
+ /* turn on dma engines */
+ tnetv_usb_reg_write(TNETV_USB_TX_CTL, 1);
+ tnetv_usb_reg_write(TNETV_USB_RX_CTL, 1);
+
+ /* enable cell dma */
+ tnetv_usb_reg_write(TNETV_USB_CELL_DMA_EN, (TNETV_USB_CELL_DMA_EN_RX | TNETV_USB_CELL_DMA_EN_TX));
+}
+
+static void tnetv_udc_enable_interrupts(void)
+{
+ UsbCtrlType usb_ctl;
+ uint8_t tx_int_en, rx_int_en;
+ int ep, chan;
+
+ /* set up the system interrupts */
+ usb_ctl.val = tnetv_usb_reg_read(TNETV_USB_CTRL);
+ usb_ctl.f.vbus_int_en = 1;
+ usb_ctl.f.reset_int_en = 1;
+ usb_ctl.f.suspend_int_en = 1;
+ usb_ctl.f.resume_int_en = 1;
+ usb_ctl.f.ep0_in_int_en = 1;
+ usb_ctl.f.ep0_out_int_en = 1;
+ usb_ctl.f.setup_int_en = 1;
+ usb_ctl.f.setupow_int_en = 1;
+ tnetv_usb_reg_write(TNETV_USB_CTRL, usb_ctl.val);
+
+ /* Enable the DMA endpoint interrupts */
+ tx_int_en = 0;
+ rx_int_en = 0;
+
+ for (ep = 1; ep < USB_NUM_ENDPOINTS; ep++)
+ {
+ chan = ep - 1;
+ rx_int_en |= (1 << chan); /* OUT */
+ tx_int_en |= (1 << chan); /* IN */
+ }
+
+ /* enable rx interrupts */
+ tnetv_usb_reg_write(TNETV_USB_RX_INT_EN, rx_int_en);
+ /* enable tx interrupts */
+ tnetv_usb_reg_write(TNETV_USB_TX_INT_EN, tx_int_en);
+
+ set_vlynq_irq(true);
+}
+
+static void tnetv_udc_disable_interrupts(void)
+{
+ UsbCtrlType usb_ctl;
+
+ /* disable interrupts from linux */
+ set_vlynq_irq(false);
+
+ /* Disable Endpoint Interrupts */
+ tnetv_usb_reg_write(TNETV_USB_RX_INT_DIS, 0x3);
+ tnetv_usb_reg_write(TNETV_USB_TX_INT_DIS, 0x3);
+
+ /* Disable USB system interrupts */
+ usb_ctl.val = tnetv_usb_reg_read(TNETV_USB_CTRL);
+ usb_ctl.f.vbus_int_en = 0;
+ usb_ctl.f.reset_int_en = 0;
+ usb_ctl.f.suspend_int_en = 0;
+ usb_ctl.f.resume_int_en = 0;
+ usb_ctl.f.ep0_in_int_en = 0;
+ usb_ctl.f.ep0_out_int_en = 0;
+ usb_ctl.f.setup_int_en = 0;
+ usb_ctl.f.setupow_int_en = 0;
+ tnetv_usb_reg_write(TNETV_USB_CTRL, usb_ctl.val);
+}
+
+static void tnetv_ep_halt(int epn, bool in)
+{
+ if (in)
+ {
+ tnetv_usb_reg_write(TNETV_USB_EPx_IN_CNT(epn), 0x80008000);
+ }
+ else
+ {
+ tnetv_usb_reg_write(TNETV_USB_EPx_OUT_CNT(epn), 0x80008000);
+ }
+}
+
+/* Reset the TNETV usb2.0 controller and configure it to run in function mode */
+static void tnetv_usb_reset(void)
+{
+ uint32_t timeout = 0;
+ int wd;
+ int ch;
+
+ /* configure function clock */
+ tnetv_usb_reg_write(TNETV_V2USB_CLK_CFG, 0x80);
+
+ /* Reset the USB 2.0 function module */
+ tnetv_usb_reg_write(TNETV_V2USB_RESET, 0x01);
+
+ /* now poll the module ready register until the 2.0 controller finishes resetting */
+ while (!(tnetv_usb_reg_read(TNETV_USB_RESET_CMPL) & 0x1) && (timeout < 1000000))
+ {
+ timeout++;
+ }
+
+ if (!(tnetv_usb_reg_read(TNETV_USB_RESET_CMPL) & 0x1))
+ {
+ logf("tnetv105_udc: VLYNQ USB module reset failed!\n");
+ return;
+ }
+
+ /* turn off external clock */
+ tnetv_usb_reg_write(TNETV_V2USB_CLK_PERF, 0);
+
+ /* clear out USB data memory */
+ for (wd = 0; wd < TNETV_EP_DATA_SIZE; wd += 4)
+ {
+ tnetv_usb_reg_write(TNETV_EP_DATA_ADDR(wd), 0);
+ }
+
+ /* clear out DMA memory */
+ for (ch = 0; ch < TNETV_DMA_NUM_CHANNELS; ch++)
+ {
+ for (wd = 0; wd < TNETV_DMA_TX_NUM_WORDS; wd++)
+ {
+ tnetv_usb_reg_write(TNETV_DMA_TX_STATE(ch, wd), 0);
+ }
+
+ for (wd = 0; wd < TNETV_DMA_RX_NUM_WORDS; wd++)
+ {
+
+ tnetv_usb_reg_write(TNETV_DMA_RX_STATE(ch, wd), 0);
+ }
+ }
+
+ /* point VLYNQ interrupts at the pending register */
+ VL_INTPTR = DM320_VLYNQ_INTPND_PHY;
+
+ /* point VLYNQ remote interrupts at the pending register */
+ VL_INTPTR_R = 0;
+
+ /* clear out interrupt register */
+ VL_INTST |= 0xFFFFFFFF;
+
+ /* enable interrupts on remote device */
+ VL_CTRL_R |= (DM320_VLYNQ_CTRL_INT_EN);
+ VL_INTVEC30_R = 0x8180;
+
+ /* enable VLYNQ interrupts & set interrupts to trigger VLYNQ int */
+ VL_CTRL |= (DM320_VLYNQ_CTRL_INT_LOC | DM320_VLYNQ_CTRL_INT_CFG);
+}
+
+static int tnetv_ep_start_xmit(int epn, void *buf, int size)
+{
+ UsbEp0ByteCntType ep0Cnt;
+
+ if (epn == 0)
+ {
+ /* Write the Control Data packet to the EP0 IN memory area */
+ tnetv_copy_to_data_mem(TNETV_EP_DATA_ADDR(EP0_INPKT_ADDRESS), buf, size);
+
+ /* start xmitting */
+ ep0Cnt.val = tnetv_usb_reg_read(TNETV_USB_EP0_CNT);
+ ep0Cnt.f.in_xbuf_cnt = size;
+ ep0Cnt.f.in_xbuf_nak = 0;
+ tnetv_usb_reg_write(TNETV_USB_EP0_CNT, ep0Cnt.val);
+ }
+ else
+ {
+ dma_addr_t buffer = (dma_addr_t)buf;
+ int send_zlp = 0;
+ if (size == 0)
+ {
+ /* Any address in SDRAM will do, contents do not matter */
+ buffer = CONFIG_SDRAM_START;
+ size = 1;
+ send_zlp = 1;
+ }
+ else
+ {
+ commit_discard_dcache_range(buf, size);
+ }
+ if ((buffer >= CONFIG_SDRAM_START) && (buffer + size < CONFIG_SDRAM_START + SDRAM_SIZE))
+ {
+ if (tnetv_cppi_send(&cppi, (epn - 1), buffer, size, send_zlp))
+ {
+ panicf("tnetv_cppi_send() failed");
+ }
+ }
+ else
+ {
+ panicf("USB xmit buf outside SDRAM %p", buf);
+ }
+ }
+
+ return 0;
+}
+
+static void tnetv_gadget_req_nuke(int epn, bool in)
+{
+ struct ep_runtime_t *ep = &ep_runtime[epn];
+ uint32_t old_rx_int = 0;
+ uint32_t old_tx_int = 0;
+ int ch;
+ int flags;
+
+ /* don't nuke control ep */
+ if (epn == 0)
+ {
+ return;
+ }
+
+ flags = disable_irq_save();
+
+ /* save and disable interrupts before nuking request */
+ old_rx_int = tnetv_usb_reg_read(TNETV_USB_RX_INT_EN);
+ old_tx_int = tnetv_usb_reg_read(TNETV_USB_TX_INT_EN);
+ tnetv_usb_reg_write(TNETV_USB_RX_INT_DIS, 0x3);
+ tnetv_usb_reg_write(TNETV_USB_TX_INT_DIS, 0x3);
+
+ ch = epn - 1;
+
+ if (in)
+ {
+ tnetv_cppi_flush_tx_queue(&cppi, ch);
+
+ tnetv_usb_reg_write(TNETV_USB_EPx_IN_CNT(epn), 0x80008000);
+ if (ep->tx_remaining > 0)
+ {
+ usb_core_transfer_complete(epn, USB_DIR_IN, -1, 0);
+ }
+ ep->tx_buf = NULL;
+ ep->tx_remaining = 0;
+ ep->tx_size = 0;
+
+ if (ep->block)
+ {
+ semaphore_release(&ep->complete);
+ ep->block = false;
+ }
+ }
+ else
+ {
+ tnetv_cppi_flush_rx_queue(&cppi, ch);
+
+ tnetv_usb_reg_write(TNETV_USB_EPx_OUT_CNT(epn), 0x80008000);
+ if (ep->rx_remaining > 0)
+ {
+ usb_core_transfer_complete(epn, USB_DIR_OUT, -1, 0);
+ }
+ ep->rx_buf = NULL;
+ ep->rx_remaining = 0;
+ ep->rx_size = 0;
+ }
+
+ /* reenable any interrupts */
+ tnetv_usb_reg_write(TNETV_USB_RX_INT_EN, old_rx_int);
+ tnetv_usb_reg_write(TNETV_USB_TX_INT_EN, old_tx_int);
+
+ restore_irq(flags);
+}
+
+static int tnetv_gadget_ep_enable(int epn, bool in)
+{
+ UsbEpCfgCtrlType epCfg;
+ int flags;
+ enum usb_device_speed speed;
+
+ if (epn == 0 || epn >= USB_NUM_ENDPOINTS)
+ {
+ return 0;
+ }
+
+ flags = disable_irq_save();
+
+ /* set the maxpacket for this endpoint based on the current speed */
+ speed = usb_drv_port_speed() ? USB_SPEED_HIGH : USB_SPEED_FULL;
+ ep_runtime[epn].max_packet_size = MAX_PACKET(epn, speed);
+
+ /* Enable the endpoint */
+ epCfg.val = tnetv_usb_reg_read(TNETV_USB_EPx_CFG(epn));
+ if (in)
+ {
+ epCfg.f.in_en = 1;
+ epCfg.f.in_stall = 0;
+ epCfg.f.in_toggle_rst = 1;
+ epCfg.f.in_buf_size = ep_runtime[epn].max_packet_size >> 3;
+ tnetv_usb_reg_write(TNETV_USB_EPx_IN_CNT(epn), 0x80008000);
+ }
+ else
+ {
+ epCfg.f.out_en = 1;
+ epCfg.f.out_stall = 0;
+ epCfg.f.out_toggle_rst = 1;
+ epCfg.f.out_buf_size = ep_runtime[epn].max_packet_size >> 3;
+ tnetv_usb_reg_write(TNETV_USB_EPx_OUT_CNT(epn), 0x80008000);
+ }
+ tnetv_usb_reg_write(TNETV_USB_EPx_CFG(epn), epCfg.val);
+
+ restore_irq(flags);
+
+ return 0;
+}
+
+static int tnetv_gadget_ep_disable(int epn, bool in)
+{
+ UsbEpCfgCtrlType epCfg;
+ int flags;
+
+ if (epn == 0 || epn >= USB_NUM_ENDPOINTS)
+ {
+ return 0;
+ }
+
+ flags = disable_irq_save();
+
+ /* Disable the endpoint */
+ epCfg.val = tnetv_usb_reg_read(TNETV_USB_EPx_CFG(epn));
+ if (in)
+ {
+ epCfg.f.in_en = 0;
+ }
+ else
+ {
+ epCfg.f.out_en = 0;
+ }
+ tnetv_usb_reg_write(TNETV_USB_EPx_CFG(epn), epCfg.val);
+
+ /* Turn off the endpoint and unready it */
+ tnetv_ep_halt(epn, in);
+
+ restore_irq(flags);
+
+ /* Clear out all the pending requests */
+ tnetv_gadget_req_nuke(epn, in);
+
+ return 0;
+}
+
+/* TNETV udc goo
+ * Power up and enable the udc. This includes resetting the hardware, turn on the appropriate clocks
+ * and initializing things so that the first setup packet can be received.
+ */
+static void tnetv_udc_enable(void)
+{
+ /* Enable M48XI crystal resonator */
+ IO_CLK_LPCTL1 &= ~(0x01);
+
+ /* Set GIO33 as CLKOUT1B */
+ IO_GIO_FSEL3 |= 0x0003;
+
+ if (tnetv_xcvr_on())
+ return;
+
+ tnetv_usb_reset();
+
+ /* BEN - RNDIS mode is assuming zlps after packets that are multiples of buffer endpoints
+ * zlps are not required by the spec and many controllers don't send them.
+ * set DMA to RNDIS mode (packet concatenation, less interrupts)
+ * tnetv_usb_reg_write(TNETV_USB_RNDIS_MODE, 0xFF);
+ */
+ tnetv_usb_reg_write(TNETV_USB_RNDIS_MODE, 0);
+
+ tnetv_init_endpoints();
+
+ tnetv_udc_enable_interrupts();
+}
+
+static void tnetv_udc_disable(void)
+{
+ tnetv_udc_disable_interrupts();
+
+ tnetv_hw_reset();
+
+ tnetv_xcvr_off();
+
+ /* Set GIO33 as normal output, drive it low */
+ IO_GIO_FSEL3 &= ~(0x0003);
+ IO_GIO_BITCLR2 = (1 << 1);
+
+ /* Disable M48XI crystal resonator */
+ IO_CLK_LPCTL1 |= 0x01;
+}
+
+static void tnetv_udc_handle_reset(void)
+{
+ UsbCtrlType usbCtrl;
+
+ /* disable USB interrupts */
+ tnetv_udc_disable_interrupts();
+
+ usbCtrl.val = tnetv_usb_reg_read(TNETV_USB_CTRL);
+ usbCtrl.f.func_addr = 0;
+ tnetv_usb_reg_write(TNETV_USB_CTRL, usbCtrl.val);
+
+ /* Reset endpoints */
+ tnetv_init_endpoints();
+
+ /* Re-enable interrupts */
+ tnetv_udc_enable_interrupts();
+}
+
+static void ep_write(int epn)
+{
+ struct ep_runtime_t *ep = &ep_runtime[epn];
+ int tx_size;
+ if (epn == 0)
+ {
+ tx_size = MIN(ep->max_packet_size, ep->tx_remaining);
+ }
+ else
+ {
+ /* DMA takes care of splitting the buffer into packets,
+ * but only up to CPPI_MAX_FRAG. After the data is sent
+ * a single interrupt is generated. There appears to be
+ * splitting code in the tnetv_cppi_send() function but
+ * it is somewhat suspicious (it doesn't seem like it
+ * will work with requests larger than 2*CPPI_MAX_FRAG).
+ * Also, if tnetv_cppi_send() does the splitting, we will
+ * get an interrupt after CPPI_MAX_FRAG but before the
+ * full request is sent.
+ *
+ * CPPI_MAX_FRAG is multiple of both 64 and 512 so we
+ * don't have to worry about this split prematurely ending
+ * the transfer.
+ */
+ tx_size = MIN(CPPI_MAX_FRAG, ep->tx_remaining);
+ }
+ tnetv_ep_start_xmit(epn, ep->tx_buf, tx_size);
+ ep->tx_remaining -= tx_size;
+ ep->tx_buf += tx_size;
+}
+
+static void in_interrupt(int epn)
+{
+ struct ep_runtime_t *ep = &ep_runtime[epn];
+
+ if (ep->tx_remaining <= 0)
+ {
+ usb_core_transfer_complete(epn, USB_DIR_IN, 0, ep->tx_size);
+ /* release semaphore for blocking transfer */
+ if (ep->block)
+ {
+ semaphore_release(&ep->complete);
+ ep->tx_buf = NULL;
+ ep->tx_size = 0;
+ ep->tx_remaining = 0;
+ ep->block = false;
+ }
+ }
+ else if (ep->tx_buf)
+ {
+ ep_write(epn);
+ }
+}
+
+static void ep_read(int epn)
+{
+ if (epn == 0)
+ {
+ UsbEp0ByteCntType ep0Cnt;
+ ep0Cnt.val = tnetv_usb_reg_read(TNETV_USB_EP0_CNT);
+ ep0Cnt.f.out_xbuf_nak = 0;
+ tnetv_usb_reg_write(TNETV_USB_EP0_CNT, ep0Cnt.val);
+ }
+ else
+ {
+ struct ep_runtime_t *ep = &ep_runtime[epn];
+ tnetv_cppi_rx_queue_add(&cppi, (epn - 1), 0, ep->rx_remaining);
+ }
+}
+
+static void out_interrupt(int epn)
+{
+ struct ep_runtime_t *ep = &ep_runtime[epn];
+ int is_short;
+ int rcv_len;
+
+ if (epn == 0)
+ {
+ UsbEp0ByteCntType ep0Cnt;
+
+ /* get the length of the received data */
+ ep0Cnt.val = tnetv_usb_reg_read(TNETV_USB_EP0_CNT);
+ rcv_len = ep0Cnt.f.out_xbuf_cnt;
+
+ if (rcv_len > ep->rx_remaining)
+ {
+ rcv_len = ep->rx_remaining;
+ }
+
+ tnetv_copy_from_data_mem(ep->rx_buf, TNETV_EP_DATA_ADDR(EP0_OUTPKT_ADDRESS), rcv_len);
+ ep->rx_buf += rcv_len;
+ ep->rx_remaining -= rcv_len;
+
+ /* See if we are done */
+ is_short = rcv_len && (rcv_len < ep->max_packet_size);
+ if (is_short || (ep->rx_remaining == 0))
+ {
+ usb_core_transfer_complete(epn, USB_DIR_OUT, 0, ep->rx_size - ep->rx_remaining);
+ ep->rx_remaining = 0;
+ ep->rx_size = 0;
+ ep->rx_buf = 0;
+ return;
+ }
+
+ /* make sure nak is cleared only if we expect more data */
+ ep0Cnt.f.out_xbuf_nak = 0;
+ tnetv_usb_reg_write(TNETV_USB_EP0_CNT, ep0Cnt.val);
+ ep_read(epn);
+ }
+ else if (ep->rx_remaining > 0)
+ {
+ int ret, bytes_rcvd;
+
+ /* copy the data from the DMA buffers */
+ bytes_rcvd = ep->rx_remaining;
+ ret = tnetv_cppi_rx_int_recv(&cppi, (epn - 1), &bytes_rcvd, ep->rx_buf, ep->max_packet_size);
+ if (ret == 0 || ret == -EAGAIN)
+ {
+ ep->rx_buf += bytes_rcvd;
+ ep->rx_remaining -= bytes_rcvd;
+ }
+
+ /* complete the request if we got a short packet or an error
+ * make sure we don't complete a request with zero bytes.
+ */
+ if ((ret == 0) && (ep->rx_remaining != ep->rx_size))
+ {
+ usb_core_transfer_complete(epn, USB_DIR_OUT, 0, ep->rx_size - ep->rx_remaining);
+ ep->rx_remaining = 0;
+ ep->rx_size = 0;
+ ep->rx_buf = 0;
+ }
+ }
+}
+
+static bool tnetv_handle_cppi(void)
+{
+ int ret;
+ int ch;
+ uint32_t tx_intstatus;
+ uint32_t rx_intstatus;
+ uint32_t status;
+ int rcv_sched = 0;
+
+ rx_intstatus = tnetv_usb_reg_read(TNETV_USB_RX_INT_STATUS);
+ tx_intstatus = tnetv_usb_reg_read(TNETV_USB_TX_INT_STATUS);
+
+ /* handle any transmit interrupts */
+ status = tx_intstatus;
+ for (ch = 0; ch < CPPI_NUM_CHANNELS && status; ch++)
+ {
+ if (status & 0x1)
+ {
+ ret = tnetv_cppi_tx_int(&cppi, ch);
+ if (ret >= 0)
+ {
+ in_interrupt(ch + 1);
+ }
+ }
+
+ status = status >> 1;
+ }
+
+ rcv_sched = 0;
+ status = rx_intstatus;
+ for (ch = 0; ch < CPPI_NUM_CHANNELS; ch++)
+ {
+ if (status & 0x1 || tnetv_cppi_rx_int_recv_check(&cppi, ch))
+ {
+ ret = tnetv_cppi_rx_int(&cppi, ch);
+ if (ret < 0)
+ {
+ /* only an error if interrupt bit is set */
+ logf("CPPI Rx: failed to ACK int!\n");
+ }
+ else
+ {
+ if (tnetv_cppi_rx_int_recv_check(&cppi, ch))
+ {
+ out_interrupt(ch + 1);
+ }
+ }
+ }
+
+ if (tnetv_cppi_rx_int_recv_check(&cppi, ch))
+ {
+ rcv_sched = 1;
+ }
+
+ status = status >> 1;
+ }
+
+ rx_intstatus = tnetv_usb_reg_read(TNETV_USB_RX_INT_STATUS);
+ tx_intstatus = tnetv_usb_reg_read(TNETV_USB_TX_INT_STATUS);
+
+ if (rx_intstatus || tx_intstatus || rcv_sched)
+ {
+ /* Request calling again after short delay
+ * Needed when for example when OUT endpoint has pending data
+ * but the USB task did not call usb_drv_recv_nonblocking() yet.
+ */
+ return true;
+ }
+ return false;
+}
+
+static int cppi_timeout_cb(struct timeout *tmo)
+{
+ (void)tmo;
+ int flags = disable_irq_save();
+ bool requeue = tnetv_handle_cppi();
+ restore_irq(flags);
+ return requeue ? 1 : 0;
+}
+
+void VLYNQ(void) __attribute__ ((section(".icode")));
+void VLYNQ(void)
+{
+ UsbStatusType sysIntrStatus;
+ UsbStatusType sysIntClear;
+ UsbCtrlType usbCtrl;
+ volatile uint32_t *reg;
+ uint32_t vlynq_intr;
+
+ /* Clear interrupt */
+ IO_INTC_IRQ1 = (1 << 0);
+
+ /* clear out VLYNQ interrupt register */
+ vlynq_intr = VL_INTST;
+
+ if (vlynq_intr & VLYNQ_INTR_USB20)
+ {
+ VL_INTST = VLYNQ_INTR_USB20;
+
+ /* Examine system interrupt status */
+ sysIntrStatus.val = tnetv_usb_reg_read(TNETV_USB_STATUS);
+
+ if (sysIntrStatus.f.reset)
+ {
+ sysIntClear.val = 0;
+ sysIntClear.f.reset = 1;
+ tnetv_usb_reg_write(TNETV_USB_STATUS, sysIntClear.val);
+
+ tnetv_udc_handle_reset();
+ usb_core_bus_reset();
+ }
+
+ if (sysIntrStatus.f.suspend)
+ {
+ sysIntClear.val = 0;
+ sysIntClear.f.suspend = 1;
+ tnetv_usb_reg_write(TNETV_USB_STATUS, sysIntClear.val);
+ }
+
+ if (sysIntrStatus.f.resume)
+ {
+ sysIntClear.val = 0;
+ sysIntClear.f.resume = 1;
+ tnetv_usb_reg_write(TNETV_USB_STATUS, sysIntClear.val);
+ }
+
+ if (sysIntrStatus.f.vbus)
+ {
+ sysIntClear.val = 0;
+ sysIntClear.f.vbus = 1;
+ tnetv_usb_reg_write(TNETV_USB_STATUS, sysIntClear.val);
+
+ if (*((uint32_t *) TNETV_USB_IF_STATUS) & 0x40)
+ {
+ /* write out connect bit */
+ reg = (volatile uint32_t *) TNETV_USB_CTRL;
+ *reg |= 0x80;
+
+ /* write to wakeup bit in clock config */
+ reg = (volatile uint32_t *) TNETV_V2USB_CLK_WKUP;
+ *reg |= TNETV_V2USB_CLK_WKUP_VBUS;
+ }
+ else
+ {
+ /* clear out connect bit */
+ reg = (volatile uint32_t *) TNETV_USB_CTRL;
+ *reg &= ~0x80;
+ }
+ }
+
+ if (sysIntrStatus.f.setup_ow)
+ {
+ sysIntrStatus.f.setup_ow = 0;
+ sysIntClear.val = 0;
+ sysIntClear.f.setup_ow = 1;
+ tnetv_usb_reg_write(TNETV_USB_STATUS, sysIntClear.val);
+ }
+ if (sysIntrStatus.f.setup)
+ {
+ UsbEp0ByteCntType ep0Cnt;
+ static struct usb_ctrlrequest setup;
+
+ sysIntrStatus.f.setup = 0;
+
+ /* Copy setup packet into buffer */
+ tnetv_copy_from_data_mem(&setup, TNETV_EP_DATA_ADDR(EP0_OUTPKT_ADDRESS), sizeof(setup));
+
+ /* Determine next stage of the control message */
+ if (setup.bRequestType & USB_DIR_IN)
+ {
+ /* This is a control-read. Switch directions to send the response.
+ * set the dir bit before clearing the interrupt
+ */
+ usbCtrl.val = tnetv_usb_reg_read(TNETV_USB_CTRL);
+ usbCtrl.f.dir = 1;
+ tnetv_usb_reg_write(TNETV_USB_CTRL, usbCtrl.val);
+ }
+ else
+ {
+ /* This is a control-write. Remain using USB_DIR_OUT to receive the rest of the data.
+ * set the NAK bits according to supplement doc
+ */
+ ep0Cnt.val = 0;
+ ep0Cnt.f.in_xbuf_nak = 1;
+ ep0Cnt.f.out_xbuf_nak = 1;
+ tnetv_usb_reg_write(TNETV_USB_EP0_CNT, ep0Cnt.val);
+
+ /* clear the dir bit before clearing the interrupt */
+ usbCtrl.val = tnetv_usb_reg_read(TNETV_USB_CTRL);
+ usbCtrl.f.dir = 0;
+ tnetv_usb_reg_write(TNETV_USB_CTRL, usbCtrl.val);
+ }
+
+ /* Clear interrupt */
+ sysIntClear.val = 0;
+ sysIntClear.f.setup = 1;
+ tnetv_usb_reg_write(TNETV_USB_STATUS, sysIntClear.val);
+
+ if (((setup.bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) &&
+ (setup.bRequest == USB_REQ_SET_ADDRESS))
+ {
+ /* Rockbox USB core works according to USB specification, i.e.
+ * it first acknowledges the control transfer and then sets
+ * the address. However, Linux TNETV105 driver first sets the
+ * address and then acknowledges the transfer. At first,
+ * it seemed that Linux driver was wrong, but it seems that
+ * TNETV105 simply requires such order. It might be documented
+ * in the datasheet and thus there is no comment in the Linux
+ * driver about this.
+ */
+ setup_is_set_address = true;
+ }
+ else
+ {
+ setup_is_set_address = false;
+ }
+
+ /* Process control packet */
+ usb_core_legacy_control_request(&setup);
+ }
+
+ if (sysIntrStatus.f.ep0_in_ack)
+ {
+ sysIntClear.val = 0;
+ sysIntClear.f.ep0_in_ack = 1;
+ tnetv_usb_reg_write(TNETV_USB_STATUS, sysIntClear.val);
+
+ in_interrupt(0);
+ }
+
+ if (sysIntrStatus.f.ep0_out_ack)
+ {
+ sysIntClear.val = 0;
+ sysIntClear.f.ep0_out_ack = 1;
+ tnetv_usb_reg_write(TNETV_USB_STATUS, sysIntClear.val);
+
+ out_interrupt(0);
+ }
+ }
+
+ if (vlynq_intr & VLYNQ_INTR_CPPI)
+ {
+ static struct timeout cppi_timeout;
+
+ VL_INTST = VLYNQ_INTR_CPPI;
+
+ if (tnetv_handle_cppi())
+ {
+ timeout_register(&cppi_timeout, cppi_timeout_cb, 1, 0);
+ }
+ }
+}
+
+void usb_charging_maxcurrent_change(int maxcurrent)
+{
+ uint32_t wreg;
+
+ if (!is_tnetv_reset_high())
+ {
+ /* TNETV105 is in reset, it is not getting more than 100 mA */
+ return;
+ }
+
+ wreg = tnetv_usb_reg_read(TNETV_V2USB_GPIO_DOUT);
+ if (maxcurrent < 500)
+ {
+ /* set tnetv into low power mode */
+ tnetv_usb_reg_write(TNETV_V2USB_GPIO_DOUT, (wreg & ~0x2));
+ }
+ else
+ {
+ /* set tnetv into high power mode */
+ tnetv_usb_reg_write(TNETV_V2USB_GPIO_DOUT, (wreg | 0x2));
+ }
+}
+
+void usb_drv_init(void)
+{
+ int epn;
+ memset(ep_runtime, 0, sizeof(ep_runtime));
+ ep_runtime[0].max_packet_size = EP0_MAX_PACKET_SIZE;
+ ep_runtime[0].in_allocated = true;
+ ep_runtime[0].out_allocated = true;
+ for (epn = 0; epn < USB_NUM_ENDPOINTS; epn++)
+ {
+ semaphore_init(&ep_runtime[epn].complete, 1, 0);
+ }
+ tnetv_cppi_init(&cppi);
+ tnetv_udc_enable();
+}
+
+void usb_drv_exit(void)
+{
+ tnetv_udc_disable();
+ tnetv_cppi_cleanup(&cppi);
+}
+
+void usb_drv_stall(int endpoint, bool stall, bool in)
+{
+ int epn = EP_NUM(endpoint);
+
+ if (epn == 0)
+ {
+ UsbEp0CtrlType ep0Ctrl;
+ ep0Ctrl.val = tnetv_usb_reg_read(TNETV_USB_EP0_CFG);
+ if (in)
+ {
+ ep0Ctrl.f.in_stall = stall ? 1 : 0;
+ }
+ else
+ {
+ ep0Ctrl.f.out_stall = stall ? 1 : 0;
+ }
+ tnetv_usb_reg_write(TNETV_USB_EP0_CFG, ep0Ctrl.val);
+ }
+ else
+ {
+ UsbEpCfgCtrlType epCfg;
+ epCfg.val = tnetv_usb_reg_read(TNETV_USB_EPx_CFG(epn));
+ if (in)
+ {
+ epCfg.f.in_stall = stall ? 1 : 0;
+ }
+ else
+ {
+ epCfg.f.out_stall = stall ? 1 : 0;
+ }
+ tnetv_usb_reg_write(TNETV_USB_EPx_CFG(epn), epCfg.val);
+ }
+}
+
+bool usb_drv_stalled(int endpoint, bool in)
+{
+ int epn = EP_NUM(endpoint);
+ if (epn == 0)
+ {
+ UsbEp0CtrlType ep0Ctrl;
+ ep0Ctrl.val = tnetv_usb_reg_read(TNETV_USB_EP0_CFG);
+ if (in)
+ {
+ return ep0Ctrl.f.in_stall;
+ }
+ else
+ {
+ return ep0Ctrl.f.out_stall;
+ }
+ }
+ else
+ {
+ UsbEpCfgCtrlType epCfg;
+ epCfg.val = tnetv_usb_reg_read(TNETV_USB_EPx_CFG(epn));
+ if (in)
+ {
+ return epCfg.f.in_stall;
+ }
+ else
+ {
+ return epCfg.f.out_stall;
+ }
+ }
+}
+
+static int _usb_drv_send(int endpoint, void *ptr, int length, bool block)
+{
+ int epn = EP_NUM(endpoint);
+ struct ep_runtime_t *ep;
+ int flags;
+
+ ep = &ep_runtime[epn];
+
+ flags = disable_irq_save();
+ ep->tx_buf = ptr;
+ ep->tx_remaining = ep->tx_size = length;
+ ep->block = block;
+ ep_write(epn);
+ restore_irq(flags);
+
+ /* wait for transfer to end */
+ if (block)
+ {
+ semaphore_wait(&ep->complete, TIMEOUT_BLOCK);
+ }
+ return 0;
+}
+
+int usb_drv_send(int endpoint, void* ptr, int length)
+{
+ if ((EP_NUM(endpoint) == 0) && (length == 0))
+ {
+ if (setup_is_set_address)
+ {
+ /* usb_drv_set_address() will call us later */
+ return 0;
+ }
+ /* HACK: Do not wait for status stage ZLP
+ * This seems to be the only way to get through SET ADDRESS
+ * and retain ability to receive SETUP packets.
+ */
+ return _usb_drv_send(endpoint, ptr, length, false);
+ }
+ return _usb_drv_send(endpoint, ptr, length, false);
+}
+
+int usb_drv_send_nonblocking(int endpoint, void* ptr, int length)
+{
+ return _usb_drv_send(endpoint, ptr, length, false);
+}
+
+int usb_drv_recv_nonblocking(int endpoint, void* ptr, int length)
+{
+ int epn = EP_NUM(endpoint);
+ struct ep_runtime_t *ep;
+ int flags;
+
+ ep = &ep_runtime[epn];
+
+ flags = disable_irq_save();
+ ep->rx_buf = ptr;
+ ep->rx_remaining = ep->rx_size = length;
+ ep_read(epn);
+ restore_irq(flags);
+
+ return 0;
+}
+
+void usb_drv_set_address(int address)
+{
+ UsbCtrlType usbCtrl;
+ usbCtrl.val = tnetv_usb_reg_read(TNETV_USB_CTRL);
+ usbCtrl.f.func_addr = address;
+ tnetv_usb_reg_write(TNETV_USB_CTRL, usbCtrl.val);
+
+ /* This seems to be the only working order */
+ setup_is_set_address = false;
+ usb_drv_send(EP_CONTROL, NULL, 0);
+ usb_drv_cancel_all_transfers();
+}
+
+/* return port speed FS=0, HS=1 */
+int usb_drv_port_speed(void)
+{
+ UsbCtrlType usbCtrl;
+ usbCtrl.val = tnetv_usb_reg_read(TNETV_USB_CTRL);
+ return usbCtrl.f.speed ? 1 : 0;
+}
+
+void usb_drv_cancel_all_transfers(void)
+{
+ int epn;
+ if (setup_is_set_address)
+ {
+ return;
+ }
+ for (epn = 1; epn < USB_NUM_ENDPOINTS; epn++)
+ {
+ tnetv_gadget_req_nuke(epn, false);
+ tnetv_gadget_req_nuke(epn, true);
+ }
+}
+
+static const uint8_t TestPacket[] =
+{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+ 0xAA, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE,
+ 0xEE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xBF, 0xDF,
+ 0xEF, 0xF7, 0xFB, 0xFD, 0xFC, 0x7E, 0xBF, 0xDF,
+ 0xEF, 0xF7, 0xFB, 0xFD, 0x7E
+};
+
+void usb_drv_set_test_mode(int mode)
+{
+ UsbCtrlType usbCtrl;
+ if (mode == 4)
+ {
+ volatile uint32_t *reg;
+ UsbEp0ByteCntType ep0Cnt;
+ UsbEp0CtrlType ep0Cfg;
+ uint8_t *addr;
+ size_t i;
+
+ /* set up the xnak for ep0 */
+ reg = (volatile uint32_t *) TNETV_USB_EP0_CNT;
+ *reg &= ~0xFF;
+
+ /* Setup endpoint zero */
+ ep0Cfg.val = 0;
+ ep0Cfg.f.buf_size = EP0_BUF_SIZE_64; /* must be 64 bytes for USB 2.0 */
+ ep0Cfg.f.dbl_buf = 0;
+ ep0Cfg.f.in_en = 1;
+ ep0Cfg.f.in_int_en = 0;
+ ep0Cfg.f.out_en = 0;
+ ep0Cfg.f.out_int_en = 0;
+ tnetv_usb_reg_write(TNETV_USB_EP0_CFG, ep0Cfg.val);
+
+ addr = (uint8_t *) TNETV_EP_DATA_ADDR(EP0_INPKT_ADDRESS);
+ for (i = 0; i < sizeof(TestPacket); i++)
+ {
+ *addr++ = TestPacket[i];
+ }
+
+ /* start xmitting (only 53 bytes are used) */
+ ep0Cnt.val = 0;
+ ep0Cnt.f.in_xbuf_cnt = 53;
+ ep0Cnt.f.in_xbuf_nak = 0;
+ tnetv_usb_reg_write(TNETV_USB_EP0_CNT, ep0Cnt.val);
+ }
+
+ /* write the config */
+ usbCtrl.val = tnetv_usb_reg_read(TNETV_USB_CTRL);
+ usbCtrl.f.hs_test_mode = mode;
+ usbCtrl.f.dir = 1;
+ tnetv_usb_reg_write(TNETV_USB_CTRL, usbCtrl.val);
+}
+
+int usb_drv_request_endpoint(int type, int dir)
+{
+ int epn;
+ for (epn = 1; epn < USB_NUM_ENDPOINTS; epn++)
+ {
+ if (type == ep_const_data[epn].type)
+ {
+ if ((dir == USB_DIR_IN) && (!ep_runtime[epn].in_allocated))
+ {
+ ep_runtime[epn].in_allocated = true;
+ tnetv_gadget_ep_enable(epn, true);
+ return epn | USB_DIR_IN;
+ }
+ if ((dir == USB_DIR_OUT) && (!ep_runtime[epn].out_allocated))
+ {
+ ep_runtime[epn].out_allocated = true;
+ tnetv_gadget_ep_enable(epn, false);
+ return epn | USB_DIR_OUT;
+ }
+ }
+ }
+ return -1;
+}
+
+void usb_drv_release_endpoint(int ep)
+{
+ int epn = EP_NUM(ep);
+ if (EP_DIR(ep) == DIR_IN)
+ {
+ ep_runtime[epn].in_allocated = false;
+ tnetv_gadget_ep_disable(epn, true);
+ }
+ else
+ {
+ ep_runtime[epn].out_allocated = false;
+ tnetv_gadget_ep_disable(epn, false);
+ }
+}
diff --git a/firmware/target/arm/tms320dm320/sansa-connect/tnetv105_usb_drv.h b/firmware/target/arm/tms320dm320/sansa-connect/tnetv105_usb_drv.h
new file mode 100644
index 0000000000..c31c9c6505
--- /dev/null
+++ b/firmware/target/arm/tms320dm320/sansa-connect/tnetv105_usb_drv.h
@@ -0,0 +1,335 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id: $
+ *
+ * Copyright (C) 2021 by Tomasz Moń
+ * Ported from Sansa Connect TNETV105 UDC Linux driver
+ * Copyright (c) 2005 Zermatt Systems, Inc.
+ * Written by: Ben Bostwick
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef TNETV105_USB_DRV_H
+#define TNETV105_USB_DRV_H
+
+#include <stdint.h>
+
+#define DM320_AHB_PADDR 0x00060000
+#define DM320_VLYNQ_PADDR 0x70000000
+
+/* TNETV105 Memory Map */
+#define VLYNQ_BASE (0x70000000)
+
+#define TNETV_BASE (VLYNQ_BASE)
+#define TNETV_V2USB_BASE (TNETV_BASE + 0x00000200)
+#define TNETV_WDOG_BASE (TNETV_BASE + 0x00000280)
+#define TNETV_USB_HOST_BASE (TNETV_BASE + 0x00010000)
+#define TNETV_USB_DEVICE_BASE (TNETV_BASE + 0x00020000)
+
+#define TNETV_V2USB_REG(x) (TNETV_V2USB_BASE + (x))
+
+#define TNETV_V2USB_RESET (TNETV_V2USB_REG(0x00))
+#define TNETV_V2USB_CLK_PERF (TNETV_V2USB_REG(0x04))
+#define TNETV_V2USB_CLK_MODE (TNETV_V2USB_REG(0x08))
+#define TNETV_V2USB_CLK_CFG (TNETV_V2USB_REG(0x0C))
+#define TNETV_V2USB_CLK_WKUP (TNETV_V2USB_REG(0x10))
+#define TNETV_V2USB_CLK_PWR (TNETV_V2USB_REG(0x14))
+
+#define TNETV_V2USB_PID_VID (TNETV_V2USB_REG(0x28))
+
+#define TNETV_V2USB_GPIO_DOUT (TNETV_V2USB_REG(0x40))
+#define TNETV_V2USB_GPIO_DIN (TNETV_V2USB_REG(0x44))
+#define TNETV_V2USB_GPIO_DIR (TNETV_V2USB_REG(0x48))
+#define TNETV_V2USB_GPIO_FS (TNETV_V2USB_REG(0x4C))
+#define TNETV_V2USB_GPIO_INTF (TNETV_V2USB_REG(0x50))
+#define TNETV_V2USB_GPIO_EOI (TNETV_V2USB_REG(0x54))
+
+#define TNETV_USB_DEVICE_REG(x) (TNETV_USB_DEVICE_BASE + (x))
+
+#define TNETV_USB_REV (TNETV_USB_DEVICE_REG(0x00))
+#define TNETV_USB_TX_CTL (TNETV_USB_DEVICE_REG(0x04))
+#define TNETV_USB_TX_TEARDOWN (TNETV_USB_DEVICE_REG(0x08))
+#define TNETV_USB_RX_CTL (TNETV_USB_DEVICE_REG(0x14))
+#define TNETV_USB_RX_TEARDOWN (TNETV_USB_DEVICE_REG(0x18))
+#define TNETV_USB_TX_ENDIAN_CTL (TNETV_USB_DEVICE_REG(0x40))
+#define TNETV_USB_RX_ENDIAN_CTL (TNETV_USB_DEVICE_REG(0x44))
+
+#define TNETV_USB_RX_FREE_BUF_CNT(ch) (TNETV_USB_DEVICE_REG(0x140 + ((ch) * 4)))
+
+#define TNETV_USB_TX_INT_STATUS (TNETV_USB_DEVICE_REG(0x170))
+#define TNETV_USB_TX_INT_EN (TNETV_USB_DEVICE_REG(0x178))
+#define TNETV_USB_TX_INT_DIS (TNETV_USB_DEVICE_REG(0x17C))
+#define TNETV_USB_VBUS_INT (TNETV_USB_DEVICE_REG(0x180))
+#define TNETV_USB_VBUS_EOI (TNETV_USB_DEVICE_REG(0x184))
+#define TNETV_USB_RX_INT_STATUS (TNETV_USB_DEVICE_REG(0x190))
+#define TNETV_USB_RX_INT_EN (TNETV_USB_DEVICE_REG(0x198))
+#define TNETV_USB_RX_INT_DIS (TNETV_USB_DEVICE_REG(0x19C))
+
+#define TNETV_USB_RESET_CMPL (TNETV_USB_DEVICE_REG(0x1A0))
+#define TNETV_CPPI_STATE (TNETV_USB_DEVICE_REG(0x1A4))
+
+#define TNETV_USB_STATUS (TNETV_USB_DEVICE_REG(0x200))
+#define TNETV_USB_CTRL (TNETV_USB_DEVICE_REG(0x204))
+#define TNETV_USB_IF_STATUS (TNETV_USB_DEVICE_REG(0x210))
+#define TNETV_USB_IF_ERR (TNETV_USB_DEVICE_REG(0x214))
+#define TNETV_USB_IF_SM (TNETV_USB_DEVICE_REG(0x218))
+
+#define TNETV_USB_EP0_CFG (TNETV_USB_DEVICE_REG(0x220))
+#define TNETV_USB_EP0_CNT (TNETV_USB_DEVICE_REG(0x224))
+
+#define TNETV_USB_EPx_CFG(x) (TNETV_USB_DEVICE_REG(0x220 + (0x10 * (x))))
+#define TNETV_USB_EPx_IN_CNT(x) (TNETV_USB_DEVICE_REG(0x224 + (0x10 * (x))))
+#define TNETV_USB_EPx_OUT_CNT(x) (TNETV_USB_DEVICE_REG(0x228 + (0x10 * (x))))
+#define TNETV_USB_EPx_ADR(x) (TNETV_USB_DEVICE_REG(0x22C + (0x10 * (x))))
+
+/* USB CPPI Config registers (0x300 - 0x30C) */
+#define TNETV_USB_RNDIS_MODE (TNETV_USB_DEVICE_REG(0x300))
+#define TNETV_USB_CELL_DMA_EN (TNETV_USB_DEVICE_REG(0x30C))
+
+#define TNETV_USB_RAW_INT (TNETV_USB_DEVICE_REG(0x310))
+#define TNETV_USB_RAW_EOI (TNETV_USB_DEVICE_REG(0x314))
+
+/* USB DMA setup RAM (0x800 - 0x8FF) */
+#define TNETV_DMA_BASE (TNETV_USB_DEVICE_BASE + 0x800)
+#define TNETV_DMA_TX_STATE(ch, wd) ((uint32_t *) ((TNETV_DMA_BASE) + ((ch) * 0x40) + ((wd) * 4)))
+#define TNETV_DMA_TX_CMPL(ch) ((TNETV_DMA_BASE) + ((ch) * 0x40) + 0x1C)
+
+#define TNETV_CPPI_TX_WORD_HDP 0
+
+#define TNETV_DMA_RX_STATE(ch, wd) ((uint32_t *) ((TNETV_DMA_BASE) + ((ch) * 0x40) + 0x20 + ((wd) * 4)))
+#define TNETV_DMA_RX_CMPL(ch) ((TNETV_DMA_BASE) + ((ch) * 0x40) + 0x3C)
+
+#define TNETV_CPPI_RX_WORD_HDP 1
+
+#define TNETV_DMA_NUM_CHANNELS 3
+
+#define TNETV_DMA_TX_NUM_WORDS 6
+#define TNETV_DMA_RX_NUM_WORDS 7
+
+
+/* USB Buffer RAM (0x1000 - 0x1A00) */
+#define TNETV_EP_DATA_ADDR(x) ((uint32_t *) ((TNETV_USB_DEVICE_BASE) + 0x1000 + (x)))
+
+#define TNETV_EP_DATA_SIZE (0xA00)
+
+#define TNETV_V2USB_RESET_DEV (1 << 0)
+
+#define TNETV_USB_CELL_DMA_EN_RX (1 << 0)
+#define TNETV_USB_CELL_DMA_EN_TX (1 << 1)
+
+#define TNETV_V2USB_CLK_WKUP_VBUS (1 << 12)
+
+#define DM320_VLYNQ_INTPND_PHY ((DM320_AHB_PADDR) + 0x0314)
+
+
+/* macro to convert from a linux pointer to a physical address
+ * to be sent over the VLYNQ bus. The dm320 vlynq rx registers are
+ * set up so the base address is the physical address of RAM
+ */
+#define __dma_to_vlynq_phys(addr) ((((uint32_t) (addr)) - 0x01000000))
+#define __vlynq_phys_to_dma(addr) ((((uint32_t) (addr)) + 0x01000000))
+
+//----------------------------------------------------------------------
+
+#define USB_FULL_SPEED_MAXPACKET 64
+#define USB_HIGH_SPEED_MAXPACKET 512
+
+/* WORD offsets into the data memory */
+#define EP0_MAX_PACKET_SIZE 64 /* Control ep - 64 bytes */
+#define EP1_MAX_PACKET_SIZE 512 /* Bulk ep - 512 bytes */
+#define EP2_MAX_PACKET_SIZE 512 /* Bulk ep - 512 bytes */
+#define EP3_MAX_PACKET_SIZE 64 /* Int ep - 64 bytes */
+#define EP4_MAX_PACKET_SIZE 64 /* Int ep - 64 bytes */
+
+/* BEN TODO: fix this crap */
+#define EP0_OUTPKT_ADDRESS 0
+#define EP0_INPKT_ADDRESS (EP0_MAX_PACKET_SIZE)
+#define EP1_XBUFFER_ADDRESS (EP0_MAX_PACKET_SIZE << 1)
+#define EP1_YBUFFER_ADDRESS (EP1_XBUFFER_ADDRESS + EP1_MAX_PACKET_SIZE)
+#define EP2_XBUFFER_ADDRESS (EP1_XBUFFER_ADDRESS + (EP1_MAX_PACKET_SIZE << 1))
+#define EP2_YBUFFER_ADDRESS (EP2_XBUFFER_ADDRESS + EP2_MAX_PACKET_SIZE)
+#define EP3_XBUFFER_ADDRESS (EP2_XBUFFER_ADDRESS + (EP2_MAX_PACKET_SIZE << 1))
+#define EP3_YBUFFER_ADDRESS (EP3_XBUFFER_ADDRESS + EP3_MAX_PACKET_SIZE)
+#define EP4_XBUFFER_ADDRESS (EP3_XBUFFER_ADDRESS + (EP3_MAX_PACKET_SIZE << 1))
+#define EP4_YBUFFER_ADDRESS (EP4_XBUFFER_ADDRESS + EP4_MAX_PACKET_SIZE)
+#define EP5_XBUFFER_ADDRESS (EP4_XBUFFER_ADDRESS + (EP4_MAX_PACKET_SIZE << 1))
+#define EP5_YBUFFER_ADDRESS (EP5_XBUFFER_ADDRESS + EP1_MAX_PACKET_SIZE)
+#define EP6_XBUFFER_ADDRESS (EP5_XBUFFER_ADDRESS + (EP1_MAX_PACKET_SIZE << 1))
+#define EP6_YBUFFER_ADDRESS (EP6_XBUFFER_ADDRESS + EP2_MAX_PACKET_SIZE)
+#define EP7_XBUFFER_ADDRESS (EP6_XBUFFER_ADDRESS + (EP2_MAX_PACKET_SIZE << 1))
+#define EP7_YBUFFER_ADDRESS (EP7_XBUFFER_ADDRESS + EP3_MAX_PACKET_SIZE)
+#define EP8_XBUFFER_ADDRESS (EP7_XBUFFER_ADDRESS + (EP3_MAX_PACKET_SIZE << 1))
+#define EP8_YBUFFER_ADDRESS (EP8_XBUFFER_ADDRESS + EP4_MAX_PACKET_SIZE)
+
+#define SETUP_PKT_DATA_SIZE 8
+
+#define EP0_BUF_SIZE_8 0
+#define EP0_BUF_SIZE_16 1
+#define EP0_BUF_SIZE_32 2
+#define EP0_BUF_SIZE_64 3
+
+/* USB Status register */
+typedef struct {
+ uint32_t rsvd1 : 5;
+ uint32_t ep0_out_ack : 1;
+ uint32_t rsvd2 : 1;
+ uint32_t ep0_in_ack : 1;
+ uint32_t rsvd3 : 16;
+ uint32_t setup_ow : 1;
+ uint32_t setup : 1;
+ uint32_t vbus : 1;
+ uint32_t resume : 1;
+ uint32_t suspend : 1;
+ uint32_t reset : 1;
+ uint32_t sof : 1;
+ uint32_t any_int : 1;
+} UsbStatusStruct;
+
+typedef union {
+ uint32_t val;
+ UsbStatusStruct f;
+} UsbStatusType;
+
+/* USB Function control register */
+typedef struct {
+ uint32_t dir : 1;
+ uint32_t hs_test_mode : 3;
+ uint32_t rsvd1 : 1;
+ uint32_t wkup_en : 1;
+ uint32_t low_pwr_en : 1;
+ uint32_t connect : 1;
+ uint32_t rsvd2 : 4;
+ uint32_t ep0_in_int_en : 1;
+ uint32_t ep0_out_int_en : 1;
+ uint32_t err_cnt_en : 2;
+ uint32_t func_addr : 7;
+ uint32_t speed : 1;
+ uint32_t setupow_int_en : 1;
+ uint32_t setup_int_en : 1;
+ uint32_t vbus_int_en : 1;
+ uint32_t resume_int_en : 1;
+ uint32_t suspend_int_en : 1;
+ uint32_t reset_int_en : 1;
+ uint32_t sof_int_en : 1;
+ uint32_t rsvd3 : 1;
+} UsbCtrlStruct;
+
+typedef union {
+ uint32_t val;
+ UsbCtrlStruct f;
+} UsbCtrlType;
+
+/* Endpoint 0 Control Register */
+typedef struct {
+ uint32_t buf_size : 2;
+ uint32_t in_int_en : 1;
+ uint32_t in_stall : 1;
+ uint32_t dbl_buf : 1;
+ uint32_t in_toggle : 1;
+ uint32_t in_nak_int_en : 1;
+ uint32_t in_en : 1;
+ uint32_t res3 : 10;
+ uint32_t out_int_en : 1;
+ uint32_t out_stall : 1;
+ uint32_t res4 : 1;
+ uint32_t out_toggle : 1;
+ uint32_t out_nak_int_en : 1;
+ uint32_t out_en : 1;
+ uint32_t res6 : 8;
+} UsbEp0CtrlStruct;
+
+typedef union {
+ uint32_t val;
+ UsbEp0CtrlStruct f;
+} UsbEp0CtrlType;
+
+/* Endpoint 0 current packet size register */
+typedef struct {
+ uint32_t in_xbuf_cnt : 7;
+ uint32_t in_xbuf_nak : 1;
+ uint32_t in_ybuf_cnt : 7;
+ uint32_t in_ybuf_nak : 1;
+ uint32_t out_xbuf_cnt : 7;
+ uint32_t out_xbuf_nak : 1;
+ uint32_t out_ybuf_cnt : 7;
+ uint32_t out_ybuf_nak : 1;
+} UsbEp0ByteCntStruct;
+
+typedef union {
+ uint32_t val;
+ UsbEp0ByteCntStruct f;
+} UsbEp0ByteCntType;
+
+/* Endpoint n Configuration and Control register */
+typedef struct {
+ uint32_t res1 : 1;
+ uint32_t in_toggle_rst : 1;
+ uint32_t in_ack_int : 1;
+ uint32_t in_stall : 1;
+ uint32_t in_dbl_buf : 1;
+ uint32_t in_toggle : 1;
+ uint32_t in_nak_int : 1;
+ uint32_t in_en : 1;
+ uint32_t res2 : 1;
+ uint32_t out_toggle_rst : 1;
+ uint32_t out_ack_int : 1;
+ uint32_t out_stall : 1;
+ uint32_t out_dbl_buf : 1;
+ uint32_t out_toggle : 1;
+ uint32_t out_nak_int : 1;
+ uint32_t out_en : 1;
+ uint32_t in_buf_size : 8;
+ uint32_t out_buf_size : 8;
+} UsbEpCfgCtrlStruct;
+
+typedef union {
+ uint32_t val;
+ UsbEpCfgCtrlStruct f;
+} UsbEpCfgCtrlType;
+
+/* Endpoint n XY Buffer Start Address register */
+typedef struct {
+ uint8_t xBuffStartAddrIn;
+ uint8_t yBuffStartAddrIn;
+ uint8_t xBuffStartAddrOut;
+ uint8_t yBuffStartAddrOut;
+} UsbEpStartAddrStruct;
+
+typedef union {
+ uint32_t val;
+ UsbEpStartAddrStruct f;
+} UsbEpStartAddrType;
+
+/* Endpoint n Packet Control register */
+typedef struct {
+ uint32_t xBufPacketCount : 11;
+ uint32_t res1 : 4;
+ uint32_t xbuf_nak : 1;
+ uint32_t yBufPacketCount : 11;
+ uint32_t res2 : 4;
+ uint32_t ybuf_nak : 1;
+} UsbEpByteCntStruct;
+
+typedef union {
+ uint32_t val;
+ UsbEpByteCntStruct f;
+} UsbEpByteCntType;
+
+#define tnetv_usb_reg_read(x) (*((volatile uint32_t *) (x)))
+#define tnetv_usb_reg_write(x, val) (*((volatile uint32_t *) (x)) = (uint32_t) (val))
+
+
+#endif
diff --git a/firmware/target/arm/tms320dm320/sansa-connect/usb-sansaconnect.c b/firmware/target/arm/tms320dm320/sansa-connect/usb-sansaconnect.c
index abe6622f0b..986efe374c 100644
--- a/firmware/target/arm/tms320dm320/sansa-connect/usb-sansaconnect.c
+++ b/firmware/target/arm/tms320dm320/sansa-connect/usb-sansaconnect.c
@@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id: $
*
- * Copyright (C) 2011 by Tomasz Moń
+ * Copyright (C) 2011-2021 by Tomasz Moń
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -22,52 +22,12 @@
#include "config.h"
#include "system.h"
#include "kernel.h"
-#include "usb.h"
-#ifdef HAVE_USBSTACK
-#include "usb_drv.h"
#include "usb_core.h"
-#endif
-
-static bool usb_is_connected = false;
static int usb_detect_callback(struct timeout *tmo)
{
(void)tmo;
-
- if (IO_GIO_BITSET0 & (1 << 9))
- {
- /* Set GIO33 as normal output, drive it low */
- IO_GIO_FSEL3 &= ~(0x0003);
- IO_GIO_BITCLR2 = (1 << 1);
-
- /* Disable M48XI crystal resonator */
- IO_CLK_LPCTL1 |= 0x01;
-
- /* Drive reset low */
- IO_GIO_BITCLR0 = (1 << 7);
-
- /* Disable VLYNQ clock */
- IO_CLK_MOD2 &= ~(1 << 13);
-
- usb_is_connected = false;
- }
- else
- {
- /* Enable M48XI crystal resonator */
- IO_CLK_LPCTL1 &= ~(0x01);
-
- /* Set GIO33 as CLKOUT1B */
- IO_GIO_FSEL3 |= 0x0003;
-
- /* Drive reset high */
- IO_GIO_BITSET0 = (1 << 7);
-
- /* Enable VLYNQ clock */
- IO_CLK_MOD2 |= (1 << 13);
-
- usb_is_connected = true;
- }
-
+ usb_status_event(usb_detect());
return 0;
}
@@ -82,20 +42,15 @@ void GIO9(void)
timeout_register(&usb_oneshot, usb_detect_callback, HZ, 0);
}
-bool usb_drv_connected(void)
-{
- return false;
-}
-
int usb_detect(void)
{
- if (usb_is_connected == true)
+ if (IO_GIO_BITSET0 & (1 << 9))
{
- return USB_INSERTED;
+ return USB_EXTRACTED;
}
else
{
- return USB_EXTRACTED;
+ return USB_INSERTED;
}
}
@@ -127,14 +82,18 @@ void usb_init_device(void)
/* Enable USB insert detection interrupt */
IO_INTC_EINT1 |= (1 << 14);
-
- /* Check if USB is connected */
- usb_detect_callback(NULL);
}
void usb_enable(bool on)
{
- (void)on;
+ if (on)
+ {
+ usb_core_init();
+ }
+ else
+ {
+ usb_core_exit();
+ }
}
void usb_attach(void)
diff --git a/firmware/target/arm/tms320dm320/sansa-connect/wifi-sansaconnect.c b/firmware/target/arm/tms320dm320/sansa-connect/wifi-sansaconnect.c
new file mode 100644
index 0000000000..1f201ca1c9
--- /dev/null
+++ b/firmware/target/arm/tms320dm320/sansa-connect/wifi-sansaconnect.c
@@ -0,0 +1,132 @@
+/***************************************************************************
+* __________ __ ___.
+* Open \______ \ ____ ____ | | _\_ |__ _______ ___
+* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+* \/ \/ \/ \/ \/
+* $Id$
+*
+* Copyright (C) 2021 by Tomasz Moń
+*
+* 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 "kernel.h"
+#include "system.h"
+#include "spi.h"
+#include "avr-sansaconnect.h"
+#include "libertas/if_spi_drv.h"
+
+#define IO_SERIAL0_XMIT (0x100)
+
+void libertas_spi_init(void)
+{
+ IO_GIO_DIR0 &= ~((1 << 4) /* CS */ | (1 << 3) /* reset */);
+ libertas_spi_reset(1);
+ libertas_spi_cs(1);
+
+ /* Enable the clock */
+ bitset16(&IO_CLK_MOD2, CLK_MOD2_SIF0);
+
+ /* Disable transmitter */
+ IO_SERIAL0_TX_ENABLE = 0x0001;
+
+ /* SELSDEN = 0, SLVEN = 0, SIOCLR = 0, SCLKM = 1, MSB = 1, MSSEL = 0,
+ * RATE = 1 (Bit rate = ARM clock / 2*(RATE + 1)))
+ * Boosted 148.5 MHz / 4 = 37.125 MHz
+ * Default 74.25 MHz / 4 = 18.5625 MHz
+ */
+ IO_SERIAL0_MODE = 0x0601;
+
+ /* Disable the clock */
+ bitclr16(&IO_CLK_MOD2, CLK_MOD2_SIF0);
+
+ /* Make sure the SPI clock is not inverted */
+ bitclr16(&IO_CLK_INV, CLK_INV_SIF0);
+}
+
+void libertas_spi_reset(int high)
+{
+ if (high)
+ {
+ IO_GIO_BITSET0 = (1 << 3);
+ }
+ else
+ {
+ IO_GIO_BITCLR0 = (1 << 3);
+ }
+}
+
+void libertas_spi_pd(int high)
+{
+ avr_hid_wifi_pd(high);
+}
+
+void libertas_spi_cs(int high)
+{
+ if (high)
+ {
+ IO_GIO_BITSET0 = (1 << 4);
+ }
+ else
+ {
+ IO_GIO_BITCLR0 = (1 << 4);
+ }
+}
+
+void libertas_spi_tx(const uint8_t *buf, int len)
+{
+ /* Enable the clock */
+ bitset16(&IO_CLK_MOD2, CLK_MOD2_SIF0);
+ IO_SERIAL0_TX_ENABLE = 0x0001;
+
+ while (len > 0)
+ {
+ IO_SERIAL0_TX_DATA = *(buf + 1);
+ while (IO_SERIAL0_RX_DATA & IO_SERIAL0_XMIT) {};
+ IO_SERIAL0_TX_DATA = *buf;
+ while (IO_SERIAL0_RX_DATA & IO_SERIAL0_XMIT) {};
+
+ buf += 2;
+ len -= 2;
+ }
+
+ IO_SERIAL0_TX_ENABLE = 0x0000;
+
+ /* Disable the clock */
+ bitclr16(&IO_CLK_MOD2, CLK_MOD2_SIF0);
+}
+
+void libertas_spi_rx(uint8_t *buf, int len)
+{
+ /* Enable the clock */
+ bitset16(&IO_CLK_MOD2, CLK_MOD2_SIF0);
+ IO_SERIAL0_TX_ENABLE = 0x0001;
+
+ while (len > 0)
+ {
+ uint16_t data;
+ IO_SERIAL0_TX_DATA = 0;
+ while ((data = IO_SERIAL0_RX_DATA) & IO_SERIAL0_XMIT) {};
+ *(buf + 1) = data & 0xFF;
+ IO_SERIAL0_TX_DATA = 0;
+ while ((data = IO_SERIAL0_RX_DATA) & IO_SERIAL0_XMIT) {};
+ *buf = data & 0xFF;
+
+ buf += 2;
+ len -= 2;
+ }
+
+ IO_SERIAL0_TX_ENABLE = 0x0000;
+
+ /* Disable the clock */
+ bitclr16(&IO_CLK_MOD2, CLK_MOD2_SIF0);
+}
diff --git a/firmware/target/arm/tms320dm320/sdmmc-dm320.c b/firmware/target/arm/tms320dm320/sdmmc-dm320.c
index 8818d645d8..e66a4cb3c7 100644
--- a/firmware/target/arm/tms320dm320/sdmmc-dm320.c
+++ b/firmware/target/arm/tms320dm320/sdmmc-dm320.c
@@ -71,6 +71,7 @@
#define BLOCKS_PER_BANK 0x7A7800
/* command flags for send_cmd */
+#define SDHC_RESP_FMT_MASK 0x0600
#define SDHC_RESP_FMT_NONE 0x0000
#define SDHC_RESP_FMT_1 0x0200
#define SDHC_RESP_FMT_2 0x0400
@@ -119,6 +120,7 @@ static struct sd_card_status sd_status[NUM_CARDS] =
/* Shoot for around 75% usage */
static struct mutex sd_mtx SHAREDBSS_ATTR;
+static struct semaphore data_done SHAREDBSS_ATTR;
static volatile unsigned int transfer_error[NUM_DRIVES];
/* align on cache line size */
static unsigned char aligned_buffer[UNALIGNED_NUM_SECTORS * SD_BLOCK_SIZE]
@@ -233,25 +235,20 @@ static int sd_poll_status(int st_reg_num, volatile unsigned int flag)
static int dma_wait_for_completion(void)
{
- unsigned short dma_status;
+ uint16_t dma_status;
- do
- {
- long time = current_tick;
+ semaphore_wait(&data_done, TIMEOUT_BLOCK);
- if (TIME_AFTER(time, next_yield))
- {
- long ty = current_tick;
- yield();
- next_yield = ty + MIN_YIELD_PERIOD;
- }
+ dma_status = IO_MMC_SD_DMA_STATUS1;
+ if (dma_status & MMC_DMASTAT1_TOUTDT)
+ {
+ return -EC_DATA_TIMEOUT;
+ }
- dma_status = IO_MMC_SD_DMA_STATUS1;
- if (dma_status & (1 << 13))
- {
- return -EC_DATA_TIMEOUT;
- }
- } while (dma_status & (1 << 12));
+ if (dma_status & MMC_DMASTAT1_RUNST)
+ {
+ panicf("Semaphore released while DMA still running");
+ }
return EC_OK;
}
@@ -306,16 +303,23 @@ static int sd_command(int cmd, unsigned long arg,
{
/* discard response */
}
- else if ((cmdat & SDHC_RESP_FMT_1) || (cmdat & SDHC_RESP_FMT_3))
- {
- response[0] = (IO_MMC_RESPONSE7 << 16) | IO_MMC_RESPONSE6;
- }
- else if (cmdat & SDHC_RESP_FMT_2)
+ else
{
- response[0] = (IO_MMC_RESPONSE7 << 16) | IO_MMC_RESPONSE6;
- response[1] = (IO_MMC_RESPONSE5 << 16) | IO_MMC_RESPONSE4;
- response[2] = (IO_MMC_RESPONSE3 << 16) | IO_MMC_RESPONSE2;
- response[3] = (IO_MMC_RESPONSE1 << 16) | IO_MMC_RESPONSE0;
+ switch (cmdat & SDHC_RESP_FMT_MASK)
+ {
+ case SDHC_RESP_FMT_1:
+ case SDHC_RESP_FMT_3:
+ response[0] = (IO_MMC_RESPONSE7 << 16) | IO_MMC_RESPONSE6;
+ break;
+ case SDHC_RESP_FMT_2:
+ response[0] = (IO_MMC_RESPONSE7 << 16) | IO_MMC_RESPONSE6;
+ response[1] = (IO_MMC_RESPONSE5 << 16) | IO_MMC_RESPONSE4;
+ response[2] = (IO_MMC_RESPONSE3 << 16) | IO_MMC_RESPONSE2;
+ response[3] = (IO_MMC_RESPONSE1 << 16) | IO_MMC_RESPONSE0;
+ break;
+ default:
+ break;
+ }
}
return 0;
@@ -713,8 +717,6 @@ sd_transfer_retry:
goto sd_transfer_error;
}
- count -= count_per_dma;
-
if (write == false)
{
discard_dcache_range(use_direct_dma ? buffer : aligned_buffer,
@@ -726,9 +728,6 @@ sd_transfer_retry:
}
}
- buffer += count_per_dma*SD_BLOCK_SIZE;
- start_addr += count_per_dma;
-
last_disk_activity = current_tick;
ret = sd_command(SD_STOP_TRANSMISSION, 0, SDHC_RESP_FMT_1, NULL);
@@ -742,6 +741,10 @@ sd_transfer_retry:
{
goto sd_transfer_error;
}
+
+ count -= count_per_dma;
+ buffer += count_per_dma*SD_BLOCK_SIZE;
+ start_addr += count_per_dma;
} while (count > 0);
while (1)
@@ -774,20 +777,10 @@ int sd_read_sectors(IF_MD(int card_no,) unsigned long start, int incount,
int sd_write_sectors(IF_MD(int card_no,) unsigned long start, int count,
const void* outbuf)
{
-#ifndef BOOTLOADER
#ifndef HAVE_MULTIDRIVE
const int card_no = 0;
#endif
return sd_transfer_sectors(card_no, start, count, (void*)outbuf, true);
-#else /* we don't need write support in bootloader */
-#ifdef HAVE_MULTIDRIVE
- (void)card_no;
-#endif
- (void)start;
- (void)count;
- (void)outbuf;
- return 0;
-#endif
}
int sd_init(void)
@@ -797,14 +790,13 @@ int sd_init(void)
if (!initialized)
{
mutex_init(&sd_mtx);
+ semaphore_init(&data_done, 1, 0);
initialized = true;
}
mutex_lock(&sd_mtx);
-#ifndef BOOTLOADER
enable_controller(false);
-#endif
/* based on linux/drivers/mmc/dm320mmc.c
Copyright (C) 2006 ZSI, All Rights Reserved.
@@ -813,9 +805,10 @@ int sd_init(void)
bitclr16(&IO_CLK_MOD2, CLK_MOD2_MMC);
bitset16(&IO_CLK_INV, CLK_INV_MMC);
- /* mmc module clock: 75 Mhz (AHB) / 2 = ~37.5 Mhz
- * (Frequencies above are taken from Sansa Connect's OF source code) */
- IO_CLK_DIV3 = (IO_CLK_DIV3 & 0xFF00) | 0x02; /* OF uses 1 */
+ /* mmc module clock when boosted 74.25 Mhz (AHB) / 2 = 37.125 Mhz
+ * default 37.125 (AHB) / 2 = 18.5625 MHz
+ * (Frequencies above are valid for Sansa Connect) */
+ IO_CLK_DIV3 = (IO_CLK_DIV3 & 0xFF00) | 0x01;
bitset16(&IO_CLK_MOD2, CLK_MOD2_MMC);
@@ -852,7 +845,9 @@ int sd_init(void)
#endif
#endif
- sd_select_device(1);
+ /* Enable data done interrupt */
+ IO_MMC_INT_ENABLE |= MMC_IE_EDATDNE;
+ IO_INTC_EINT1 |= INTR_EINT1_MMCSDMS0;
/* Disable Memory Card CLK - it is enabled on demand by TMS320DM320 */
bitclr16(&IO_MMC_MEM_CLK_CONTROL, (1 << 8));
@@ -895,3 +890,17 @@ int sd_event(long id, intptr_t data)
return rc;
}
+
+void SD_MMC(void) __attribute__ ((section(".icode")));
+void SD_MMC(void)
+{
+ uint16_t status = IO_MMC_STATUS0;
+
+ /* Clear interrupt */
+ IO_INTC_IRQ1 = INTR_IRQ1_MMCSDMS0;
+
+ if (status & MMC_ST0_DATDNE)
+ {
+ semaphore_release(&data_done);
+ }
+}
diff --git a/firmware/target/arm/tms320dm320/system-dm320.c b/firmware/target/arm/tms320dm320/system-dm320.c
index 93cf3c51c4..6cf616184d 100644
--- a/firmware/target/arm/tms320dm320/system-dm320.c
+++ b/firmware/target/arm/tms320dm320/system-dm320.c
@@ -312,7 +312,14 @@ void system_init(void)
#endif
{
#ifdef SANSA_CONNECT
- /* Setting AHB divisor to 0 increases power consumption */
+ /* Setting AHB divisor to 0 increases power consumption
+ * Slow Setup:
+ * ARM div = 4 ( 74.25 MHz )
+ * AHB div = 2 ( 37.125 MHz )
+ * Fast Setup:
+ * ARM div = 2 ( 148.5 MHz )
+ * AHB div = 2 ( 74.25 MHz )
+ */
clock_arm_slow = (1 << 8) | 3;
clock_arm_fast = (1 << 8) | 1;
#else
@@ -369,9 +376,11 @@ void system_init(void)
#endif
#ifdef SANSA_CONNECT
+#ifndef HAVE_WIFI
/* keep WIFI CS and reset high to save power */
IO_GIO_DIR0 &= ~((1 << 4) /* CS */ | (1 << 3) /* reset */);
IO_GIO_BITSET0 = (1 << 4) | (1 << 3);
+#endif
i2c_init();
avr_hid_init();
@@ -494,6 +503,13 @@ void udelay(int usec) {
}
}
+void mdelay(int msec)
+{
+ int ms_per_tick = 1000 / HZ;
+ /* Round up to next full tick */
+ sleep((msec + ms_per_tick - 1) / ms_per_tick);
+}
+
#ifdef BOOTLOADER
void system_prepare_fw_start(void)
{
diff --git a/firmware/target/arm/tms320dm320/system-target.h b/firmware/target/arm/tms320dm320/system-target.h
index 59ae61f8df..9aa8b3e213 100644
--- a/firmware/target/arm/tms320dm320/system-target.h
+++ b/firmware/target/arm/tms320dm320/system-target.h
@@ -24,12 +24,18 @@
#include "system-arm.h"
#include "mmu-arm.h"
-#define CPUFREQ_SLEEP 32768
+#ifdef SANSA_CONNECT
+#define CPUFREQ_DEFAULT 74250000
+#define CPUFREQ_NORMAL 74250000
+#define CPUFREQ_MAX 148500000
+#else
#define CPUFREQ_DEFAULT 87500000
#define CPUFREQ_NORMAL 87500000
#define CPUFREQ_MAX 175000000
+#endif
void udelay(int usec);
+void mdelay(int msec);
#if defined(CREATIVE_ZVx) && defined(BOOTLOADER)
/* hacky.. */
diff --git a/firmware/target/arm/usb-drv-arc.c b/firmware/target/arm/usb-drv-arc.c
index b19b635923..22751f27f0 100644
--- a/firmware/target/arm/usb-drv-arc.c
+++ b/firmware/target/arm/usb-drv-arc.c
@@ -593,7 +593,7 @@ int usb_drv_send(int endpoint, void* ptr, int length)
return prime_transfer(EP_NUM(endpoint), ptr, length, true, true);
}
-int usb_drv_recv(int endpoint, void* ptr, int length)
+int usb_drv_recv_nonblocking(int endpoint, void* ptr, int length)
{
//logf("usbrecv(%x, %d)", ptr, length);
return prime_transfer(EP_NUM(endpoint), ptr, length, false, false);
@@ -877,7 +877,7 @@ static void control_received(void)
}
}
- usb_core_control_request((struct usb_ctrlrequest*)tmp);
+ usb_core_legacy_control_request((struct usb_ctrlrequest*)tmp);
}
static void transfer_completed(void)
diff --git a/firmware/target/arm/usb-s3c6400x.c b/firmware/target/arm/usb-s3c6400x.c
index 920d1a6286..0f3ecf8c00 100644
--- a/firmware/target/arm/usb-s3c6400x.c
+++ b/firmware/target/arm/usb-s3c6400x.c
@@ -166,7 +166,7 @@ int usb_drv_send_nonblocking(int endpoint, void *ptr, int length)
return 0;
}
-int usb_drv_recv(int endpoint, void* ptr, int length)
+int usb_drv_recv_nonblocking(int endpoint, void* ptr, int length)
{
ep_transfer(EP_NUM(endpoint), ptr, length, true);
return 0;
@@ -522,7 +522,7 @@ static void handle_ep_int(int ep, bool out)
ep0_setup_pkt->bRequest == USB_REQ_SET_ADDRESS)
DCFG = (DCFG & ~bitm(DCFG, devadr)) | (ep0_setup_pkt->wValue << DCFG_devadr_bitp);
- usb_core_control_request(ep0_setup_pkt);
+ usb_core_legacy_control_request(ep0_setup_pkt);
}
}
diff --git a/firmware/target/arm/usb-tcc.c b/firmware/target/arm/usb-tcc.c
index 1b5f16c223..53f101c471 100644
--- a/firmware/target/arm/usb-tcc.c
+++ b/firmware/target/arm/usb-tcc.c
@@ -251,7 +251,7 @@ void handle_control(void)
DEBUG(2, "req: %02x %02d", req->bRequestType, req->bRequest);
}
- usb_core_control_request(req);
+ usb_core_legacy_control_request(req);
}
static
@@ -609,7 +609,7 @@ int usb_drv_send_nonblocking(int endpoint, void *ptr, int length)
return rc;
}
-int usb_drv_recv(int endpoint, void* ptr, int length)
+int usb_drv_recv_nonblocking(int endpoint, void* ptr, int length)
{
volatile struct tcc_ep *tcc_ep = &tcc_endpoints[endpoint & 0x7f];
int flags;
diff --git a/firmware/target/arm/wmcodec-telechips.c b/firmware/target/arm/wmcodec-telechips.c
index 8fb035bdfa..deaced2682 100644
--- a/firmware/target/arm/wmcodec-telechips.c
+++ b/firmware/target/arm/wmcodec-telechips.c
@@ -34,8 +34,6 @@
#if defined(COWON_D2)
/* The D2's audio codec uses an I2C address of 0x34 */
#define I2C_AUDIO_ADDRESS 0x34
-#elif defined (IAUDIO_7) || defined(LOGIK_DAX) || defined(SANSA_M200)
-#define I2C_AUDIO_ADDRESS 0x34
#else
#error wmcodec not implemented for this target!
#endif
diff --git a/firmware/target/coldfire/iaudio/m3/lcd-m3.c b/firmware/target/coldfire/iaudio/m3/lcd-m3.c
index 3da608a0ef..5e84cbacd3 100644
--- a/firmware/target/coldfire/iaudio/m3/lcd-m3.c
+++ b/firmware/target/coldfire/iaudio/m3/lcd-m3.c
@@ -258,6 +258,7 @@ void lcd_update(void)
int y;
if (initialized)
{
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
for(y = 0;y < LCD_FBHEIGHT;y++)
{
/* Copy display bitmap to hardware.
@@ -266,7 +267,7 @@ void lcd_update(void)
have to update one page at a time. */
lcd_write_command(LCD_SET_PAGE | (y > 5 ? y + 2 : y));
lcd_write_command_e(LCD_SET_COLUMN | 0, 0);
- lcd_write_data(FBADDR(0, y), LCD_WIDTH);
+ lcd_write_data(fbaddr(0,y), LCD_WIDTH);
}
}
}
@@ -289,6 +290,7 @@ void lcd_update_rect(int x, int y, int width, int height)
if(ymax >= LCD_FBHEIGHT)
ymax = LCD_FBHEIGHT-1;
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
/* Copy specified rectangle bitmap to hardware
COM48-COM63 are not connected, so we need to skip those */
for (; y <= ymax; y++)
@@ -296,7 +298,7 @@ void lcd_update_rect(int x, int y, int width, int height)
lcd_write_command(LCD_SET_PAGE | ((y > 5 ? y + 2 : y) & 0xf));
lcd_write_command_e(LCD_SET_COLUMN | ((x >> 4) & 0xf), x & 0xf);
- lcd_write_data(FBADDR(x,y), width);
+ lcd_write_data(fbaddr(x,y), width);
}
}
}
diff --git a/firmware/target/coldfire/iaudio/m5/lcd-m5.c b/firmware/target/coldfire/iaudio/m5/lcd-m5.c
index 8f022adf96..35fd173f18 100644
--- a/firmware/target/coldfire/iaudio/m5/lcd-m5.c
+++ b/firmware/target/coldfire/iaudio/m5/lcd-m5.c
@@ -200,6 +200,7 @@ void lcd_update(void)
{
int y;
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
/* Copy display bitmap to hardware */
for (y = 0; y < LCD_FBHEIGHT; y++)
{
@@ -207,7 +208,7 @@ void lcd_update(void)
lcd_write_command_ex(LCD_CNTL_COLUMN, 0, -1);
lcd_write_command(LCD_CNTL_DATA_WRITE);
- lcd_write_data (FBADDR(0, y), LCD_WIDTH);
+ lcd_write_data (fbaddr(0,y), LCD_WIDTH);
}
}
@@ -228,6 +229,7 @@ void lcd_update_rect(int x, int y, int width, int height)
if(ymax >= LCD_FBHEIGHT)
ymax = LCD_FBHEIGHT-1;
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
/* Copy specified rectange bitmap to hardware */
for (; y <= ymax; y++)
{
@@ -235,6 +237,6 @@ void lcd_update_rect(int x, int y, int width, int height)
lcd_write_command_ex(LCD_CNTL_COLUMN, x, -1);
lcd_write_command(LCD_CNTL_DATA_WRITE);
- lcd_write_data (FBADDR(x,y), width);
+ lcd_write_data (fbaddr(x,y), width);
}
}
diff --git a/firmware/target/coldfire/iriver/h100/lcd-h100.c b/firmware/target/coldfire/iriver/h100/lcd-h100.c
index b13751b9eb..b6eea1dfa3 100644
--- a/firmware/target/coldfire/iriver/h100/lcd-h100.c
+++ b/firmware/target/coldfire/iriver/h100/lcd-h100.c
@@ -209,6 +209,7 @@ void lcd_update(void)
{
int y;
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
/* Copy display bitmap to hardware */
for (y = 0; y < LCD_FBHEIGHT; y++)
{
@@ -216,7 +217,7 @@ void lcd_update(void)
lcd_write_command_ex(LCD_CNTL_COLUMN, 0, -1);
lcd_write_command(LCD_CNTL_DATA_WRITE);
- lcd_write_data (FBADDR(0, y), LCD_WIDTH);
+ lcd_write_data (fbaddr(0,y), LCD_WIDTH);
}
}
@@ -237,6 +238,7 @@ void lcd_update_rect(int x, int y, int width, int height)
if(ymax >= LCD_FBHEIGHT)
ymax = LCD_FBHEIGHT-1;
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
/* Copy specified rectange bitmap to hardware */
for (; y <= ymax; y++)
{
@@ -244,6 +246,6 @@ void lcd_update_rect(int x, int y, int width, int height)
lcd_write_command_ex(LCD_CNTL_COLUMN, x, -1);
lcd_write_command(LCD_CNTL_DATA_WRITE);
- lcd_write_data (FBADDR(x,y), width);
+ lcd_write_data (fbaddr(x,y), width);
}
}
diff --git a/firmware/target/coldfire/mpio/hd300/lcd-hd300.c b/firmware/target/coldfire/mpio/hd300/lcd-hd300.c
index 509ed4cd53..9cff9cd712 100644
--- a/firmware/target/coldfire/mpio/hd300/lcd-hd300.c
+++ b/firmware/target/coldfire/mpio/hd300/lcd-hd300.c
@@ -231,6 +231,7 @@ void lcd_update_rect(int x, int y, int width, int height)
if(ymax >= LCD_FBHEIGHT)
ymax = LCD_FBHEIGHT-1;
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
/* Copy specified rectange bitmap to hardware */
for (; y <= ymax; y++)
{
@@ -238,6 +239,6 @@ void lcd_update_rect(int x, int y, int width, int height)
lcd_write_command_ex(LCD_CNTL_COLUMN, x, -1);
lcd_write_command(LCD_CNTL_DATA_WRITE);
- lcd_write_data (FBADDR(x,y), width);
+ lcd_write_data (fbaddr(x,y), width);
}
}
diff --git a/firmware/target/coldfire/pcm-coldfire.c b/firmware/target/coldfire/pcm-coldfire.c
index c45219297c..36eb00c31b 100644
--- a/firmware/target/coldfire/pcm-coldfire.c
+++ b/firmware/target/coldfire/pcm-coldfire.c
@@ -301,21 +301,6 @@ void DMA0(void)
/* else inished playing */
} /* DMA0 */
-const void * pcm_play_dma_get_peak_buffer(int *count)
-{
- unsigned long addr, cnt;
-
- /* Make sure interrupt doesn't change the second value after we read the
- * first value. */
- int level = set_irq_level(DMA_IRQ_LEVEL);
- addr = SAR0;
- cnt = BCR0;
- restore_irq(level);
-
- *count = (cnt & 0xffffff) >> 2;
- return (void *)((addr + 2) & ~3);
-} /* pcm_play_dma_get_peak_buffer */
-
#ifdef HAVE_RECORDING
/****************************************************************************
** Recording DMA transfer
diff --git a/firmware/target/hosted/android/pcm-android.c b/firmware/target/hosted/android/pcm-android.c
index b078f92698..c23b802b2d 100644
--- a/firmware/target/hosted/android/pcm-android.c
+++ b/firmware/target/hosted/android/pcm-android.c
@@ -171,13 +171,6 @@ void pcm_play_dma_stop(void)
stop_method);
}
-const void * pcm_play_dma_get_peak_buffer(int *count)
-{
- uintptr_t addr = (uintptr_t)pcm_data_start;
- *count = pcm_data_size / 4;
- return (void *)((addr + 3) & ~3);
-}
-
void pcm_play_dma_init(void)
{
/* in order to have background music playing after leaving the activity,
diff --git a/firmware/target/hosted/backtrace-glibc.c b/firmware/target/hosted/backtrace-glibc.c
index 19becda38b..3a63499187 100644
--- a/firmware/target/hosted/backtrace-glibc.c
+++ b/firmware/target/hosted/backtrace-glibc.c
@@ -52,7 +52,7 @@ void rb_backtrace(int pc, int sp, unsigned *line)
for(int i = 0; i < count; i++)
{
- lcd_putsf(0, (*line)++, " %s", buffer[i], strings[i]);
+ lcd_putsf(0, (*line)++, " %s", strings[i]);
lcd_update();
}
diff --git a/firmware/target/hosted/filesystem-app.c b/firmware/target/hosted/filesystem-app.c
index 2121af7752..09b3365a9e 100644
--- a/firmware/target/hosted/filesystem-app.c
+++ b/firmware/target/hosted/filesystem-app.c
@@ -43,6 +43,10 @@
#undef PIVOT_ROOT
#endif
+#if defined(DBTOOL)
+#define PIVOT_ROOT "."
+#endif
+
#if defined(__PCTOOL__)
/* We don't want this for tools */
#undef HAVE_SPECIAL_DIRS
@@ -222,7 +226,7 @@ const char * handle_special_dirs(const char *dir, unsigned flags,
#define PIVOT_ROOT_LEN (sizeof(PIVOT_ROOT)-1)
/* Prepend root prefix to find actual path */
if (strncmp(PIVOT_ROOT, dir, PIVOT_ROOT_LEN)
-#ifdef MULTIDRIVE_DIR
+#if defined(MULTIDRIVE_DIR) && defined(MULTIDRIVE_DIR_LEN)
/* Unless it's a MULTIDRIVE dir, in which case use as-is */
&& strncmp(MULTIDRIVE_DIR, dir, MULTIDRIVE_DIR_LEN)
#endif
@@ -580,10 +584,11 @@ ssize_t app_readlink(const char *path, char *buf, size_t bufsiz)
int os_volume_path(IF_MV(int volume, ) char *buffer, size_t bufsize)
{
-#ifdef HAVE_MULTIVOLUME
+#if defined(HAVE_MULTIVOLUME) && !(CONFIG_PLATFORM & PLATFORM_HOSTED)
char volname[VOL_MAX_LEN + 1];
get_volume_name(volume, volname);
#else
+ IF_MV((void)volume;)
const char *volname = "/";
#endif
@@ -595,3 +600,8 @@ int os_volume_path(IF_MV(int volume, ) char *buffer, size_t bufsize)
return 0;
}
+
+const char* app_root_realpath(void)
+{
+ return PATH_ROOTSTR;
+}
diff --git a/firmware/target/hosted/filesystem-app.h b/firmware/target/hosted/filesystem-app.h
index 68b3f13b6e..b35b63e95f 100644
--- a/firmware/target/hosted/filesystem-app.h
+++ b/firmware/target/hosted/filesystem-app.h
@@ -82,6 +82,7 @@ ssize_t app_write(int fildes, const void *buf, size_t nbyte);
#endif /* HAVE_SDL_THREADS */
int app_remove(const char *path);
int app_rename(const char *old, const char *new);
+#define app_modtime os_modtime
#define app_filesize os_filesize
#define app_fsamefile os_fsamefile
int app_relate(const char *path1, const char *path2);
diff --git a/firmware/target/hosted/filesystem-hosted.h b/firmware/target/hosted/filesystem-hosted.h
index 5147ef6db6..b9c7ce648a 100644
--- a/firmware/target/hosted/filesystem-hosted.h
+++ b/firmware/target/hosted/filesystem-hosted.h
@@ -33,6 +33,7 @@ void * os_lc_open(const char *ospath);
#define _FILESYSTEM_HOSTED__FILE_H_
#ifndef OSFUNCTIONS_DECLARED
+int os_modtime(const char *path, time_t modtime);
off_t os_filesize(int osfd);
int os_fsamefile(int osfd1, int osfd2);
int os_relate(const char *path1, const char *path2);
diff --git a/firmware/target/hosted/filesystem-unix.c b/firmware/target/hosted/filesystem-unix.c
index 177cb574e0..f0d7de640d 100644
--- a/firmware/target/hosted/filesystem-unix.c
+++ b/firmware/target/hosted/filesystem-unix.c
@@ -23,6 +23,7 @@
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
+#include <utime.h>
#include "config.h"
#include "system.h"
#include "file.h"
@@ -35,6 +36,17 @@
#define SAME_FILE_INFO(sb1p, sb2p) \
((sb1p)->st_dev == (sb2p)->st_dev && (sb1p)->st_ino == (sb2p)->st_ino)
+int os_modtime(const char *path, time_t modtime)
+{
+ struct utimbuf times =
+ {
+ .actime = modtime,
+ .modtime = modtime,
+ };
+
+ return utime(path, &times);
+}
+
off_t os_filesize(int osfd)
{
struct stat sb;
diff --git a/firmware/target/hosted/filesystem-win32.c b/firmware/target/hosted/filesystem-win32.c
index be95134cc9..fac10d003b 100644
--- a/firmware/target/hosted/filesystem-win32.c
+++ b/firmware/target/hosted/filesystem-win32.c
@@ -462,6 +462,13 @@ int os_relate(const char *ospath1, const char *ospath2)
return rc;
}
+int os_modtime(const char *path, time_t modtime)
+{
+ (void)path;
+ (void)modtime;
+ return 0;
+}
+
void volume_size(IF_MV(int volume,) unsigned long *sizep, unsigned long *freep)
{
ULARGE_INTEGER free = { .QuadPart = 0 },
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/powermgmt-target.h b/firmware/target/hosted/ibasso/dx50/adc-target.h
index e69de29bb2..e69de29bb2 100644
--- a/firmware/target/mips/ingenic_x1000/fiiom3k/powermgmt-target.h
+++ b/firmware/target/hosted/ibasso/dx50/adc-target.h
diff --git a/firmware/target/hosted/ibasso/dx90/adc-target.h b/firmware/target/hosted/ibasso/dx90/adc-target.h
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/firmware/target/hosted/ibasso/dx90/adc-target.h
diff --git a/firmware/target/hosted/ibasso/pcm-ibasso.c b/firmware/target/hosted/ibasso/pcm-ibasso.c
index e31732431b..889386b7c6 100644
--- a/firmware/target/hosted/ibasso/pcm-ibasso.c
+++ b/firmware/target/hosted/ibasso/pcm-ibasso.c
@@ -123,7 +123,7 @@ static void* pcm_thread_run(void* nothing)
/* https://github.com/tinyalsa/tinyalsa/blob/master/tinypcminfo.c */
-static const char* format_lookup[] =
+static const char* const format_lookup[] =
{
/*[0] =*/ "S8",
"U8",
@@ -439,17 +439,6 @@ void pcm_dma_apply_settings(void)
}
}
-/* TODO: WTF */
-const void* pcm_play_dma_get_peak_buffer(int* count)
-{
- TRACE;
-
- uintptr_t addr = (uintptr_t) _pcm_buffer;
- *count = _pcm_buffer_size / 4;
- return (void*) ((addr + 3) & ~3);
-}
-
-
void pcm_close_device(void)
{
TRACE;
diff --git a/firmware/target/hosted/ibasso/sysfs-ibasso.c b/firmware/target/hosted/ibasso/sysfs-ibasso.c
index 8f62e3fec2..e3a0f911af 100644
--- a/firmware/target/hosted/ibasso/sysfs-ibasso.c
+++ b/firmware/target/hosted/ibasso/sysfs-ibasso.c
@@ -32,7 +32,7 @@
#include "sysfs-ibasso.h"
-static const char* SYSFS_PATHS[] =
+static const char* const SYSFS_PATHS[] =
{
/* SYSFS_DX50_CODEC_VOLUME */
"/dev/codec_volume",
diff --git a/firmware/target/hosted/ibasso/vold-ibasso.c b/firmware/target/hosted/ibasso/vold-ibasso.c
index c0cb06b6b8..e703ce0a1f 100644
--- a/firmware/target/hosted/ibasso/vold-ibasso.c
+++ b/firmware/target/hosted/ibasso/vold-ibasso.c
@@ -110,7 +110,7 @@ static void* vold_monitor_run(void* nothing)
/* Check to see if external SD is mounted */
// extsd_present = !system("mountpoint -q /mnt/external_sd");
-// extsd_present = !system("mount -o remount,rw /mnt/external_sd");
+ extsd_present = !system("mount -o remount,rw /mnt/external_sd");
vold_monitor_open_socket();
if(_vold_monitor_socket_fd < 0)
diff --git a/firmware/target/hosted/maemo/pcm-gstreamer.c b/firmware/target/hosted/maemo/pcm-gstreamer.c
index e2d57f9fab..33fa6d343f 100644
--- a/firmware/target/hosted/maemo/pcm-gstreamer.c
+++ b/firmware/target/hosted/maemo/pcm-gstreamer.c
@@ -191,14 +191,6 @@ static void feed_data(GstElement * appsrc, guint size_hint, void *unused)
unlock_audio();
}
-const void * pcm_play_dma_get_peak_buffer(int *count)
-{
- uintptr_t addr = (uintptr_t)pcm_data;
- *count = pcm_data_size / 4;
- return (void *)((addr + 2) & ~3);
-}
-
-
static gboolean
gst_bus_message (GstBus * bus, GstMessage * message, void *unused)
{
diff --git a/firmware/target/hosted/pcm-alsa.c b/firmware/target/hosted/pcm-alsa.c
index 81c0a19a45..68002d8158 100644
--- a/firmware/target/hosted/pcm-alsa.c
+++ b/firmware/target/hosted/pcm-alsa.c
@@ -68,6 +68,11 @@
#warning "MIX_FRAME_SAMPLES <1024 may cause dropouts!"
#endif
+/* PCM_DC_OFFSET_VALUE is a workaround for eros q hardware quirk */
+#if !defined(PCM_DC_OFFSET_VALUE)
+# define PCM_DC_OFFSET_VALUE 0
+#endif
+
static const snd_pcm_access_t access_ = SND_PCM_ACCESS_RW_INTERLEAVED; /* access mode */
#if defined(HAVE_ALSA_32BIT)
static const snd_pcm_format_t format = SND_PCM_FORMAT_S32_LE; /* sample format */
@@ -359,8 +364,8 @@ static bool copy_frames(bool first)
sample_t *sample_ptr = &frames[2*(period_size-frames_left)];
for (int i = 0; i < nframes; i++)
{
- *sample_ptr++ = *pcm_ptr++ * dig_vol_mult_l;
- *sample_ptr++ = *pcm_ptr++ * dig_vol_mult_r;
+ *sample_ptr++ = (*pcm_ptr++ * dig_vol_mult_l) + PCM_DC_OFFSET_VALUE;
+ *sample_ptr++ = (*pcm_ptr++ * dig_vol_mult_r) + PCM_DC_OFFSET_VALUE;
}
}
else
@@ -775,13 +780,6 @@ void pcm_play_dma_start(const void *addr, size_t size)
}
}
-const void * pcm_play_dma_get_peak_buffer(int *count)
-{
- uintptr_t addr = (uintptr_t)pcm_data;
- *count = pcm_size / 4;
- return (void *)((addr + 3) & ~3);
-}
-
void pcm_play_dma_postinit(void)
{
audiohw_postinit();
diff --git a/firmware/target/hosted/rolo.c b/firmware/target/hosted/rolo.c
index 04b21d553e..dfe483c964 100644
--- a/firmware/target/hosted/rolo.c
+++ b/firmware/target/hosted/rolo.c
@@ -37,12 +37,6 @@
//#define LOGF_ENABLE
#include "logf.h"
-#if defined(HAVE_BOOTDATA) && !defined(SIMULATOR)
-#include "bootdata.h"
-#include "crc32.h"
-extern int write_bootdata(unsigned char* buf, int len, unsigned int boot_volume); /*rb-loader.c*/
-#endif
-
static void rolo_error(const char *text, const char *text2)
{
lcd_clear_display();
@@ -72,16 +66,6 @@ int rolo_load(const char* filename)
audio_stop();
-#if defined(HAVE_BOOTDATA) && !defined(SIMULATOR)
- /* write the bootdata as if rolo were the bootloader */
- unsigned int crc = 0;
- if (strcmp(filename, BOOTDIR "/" BOOTFILE) == 0)
- crc = crc_32(boot_data.payload, boot_data.length, 0xffffffff);
-
- if(crc > 0 && crc == boot_data.crc)
- write_bootdata(filebuf, filebuf_size, boot_data.boot_volume); /* rb-loader.c */
-#endif
-
#ifdef HAVE_STORAGE_FLUSH
lcd_puts(0, 2, "Flushing storage buffers");
lcd_update();
diff --git a/firmware/target/hosted/rtc.c b/firmware/target/hosted/rtc.c
index e747aece38..05c8f75ef7 100644
--- a/firmware/target/hosted/rtc.c
+++ b/firmware/target/hosted/rtc.c
@@ -54,6 +54,9 @@ int rtc_write_datetime(const struct tm *tm)
tv.tv_sec = mktime((struct tm *)tm);
tv.tv_usec = 0;
+ if ((int)tv.tv_sec == -1)
+ return -1;
+
/* set system clock */
settimeofday(&tv, NULL);
diff --git a/firmware/target/hosted/samsungypr/radio-ypr.c b/firmware/target/hosted/samsungypr/radio-ypr.c
index 42d485231d..ef7fb84aa0 100644
--- a/firmware/target/hosted/samsungypr/radio-ypr.c
+++ b/firmware/target/hosted/samsungypr/radio-ypr.c
@@ -126,62 +126,3 @@ int fmradio_i2c_read(unsigned char address, unsigned char* buf, int count)
(void)address;
return read(radio_dev, buf, count);
}
-
-#ifdef HAVE_RDS_CAP
-
-/* Register we are going to poll */
-#define STATUSRSSI (0xA)
-#define STATUSRSSI_RDSR (0x1 << 15)
-
-/* Low-level RDS Support */
-static struct event_queue rds_queue;
-static uint32_t rds_stack[DEFAULT_STACK_SIZE / sizeof(uint32_t)];
-
-enum {
- Q_POWERUP,
-};
-
-static void NORETURN_ATTR rds_thread(void)
-{
- /* start up frozen */
- int timeout = TIMEOUT_BLOCK;
- struct queue_event ev;
- bool rds_rdy = false;
- struct si4700_dbg_info radio_regs;
-
- while (true) {
- queue_wait_w_tmo(&rds_queue, &ev, timeout);
- switch (ev.id) {
- case Q_POWERUP:
- /* power up: timeout after 1 tick, else block indefinitely */
- timeout = ev.data ? 1 : TIMEOUT_BLOCK;
- break;
- case SYS_TIMEOUT:
- /* Captures RDS data and processes it */
- si4700_dbg_info(&radio_regs);
- bool rdsr = radio_regs.regs[STATUSRSSI] & STATUSRSSI_RDSR;
- if (rdsr != rds_rdy) {
- rds_rdy = rdsr;
- if (rdsr) {
- si4700_rds_process();
- }
- }
- break;
- }
- }
-}
-
-/* true after full radio power up, and false before powering down */
-void si4700_rds_powerup(bool on)
-{
- queue_post(&rds_queue, Q_POWERUP, on);
-}
-
-/* One-time RDS init at startup */
-void si4700_rds_init(void)
-{
- queue_init(&rds_queue, false);
- create_thread(rds_thread, rds_stack, sizeof(rds_stack), 0, "rds"
- IF_PRIO(, PRIORITY_PLAYBACK) IF_COP(, CPU));
-}
-#endif /* HAVE_RDS_CAP */
diff --git a/firmware/target/hosted/sdl/lcd-sdl.c b/firmware/target/hosted/sdl/lcd-sdl.c
index de19de365a..8cbb6c5651 100644
--- a/firmware/target/hosted/sdl/lcd-sdl.c
+++ b/firmware/target/hosted/sdl/lcd-sdl.c
@@ -31,9 +31,9 @@ void sdl_update_rect(SDL_Surface *surface, int x_start, int y_start, int width,
unsigned long (*getpixel)(int, int))
{
SDL_Rect dest;
-#if LCD_DEPTH >= 8 && (LCD_PIXELFORMAT == RGB565) \
- && !defined(LCD_STRIDEFORMAT) && !defined(HAVE_LCD_SPLIT) \
- && !defined(HAVE_REMOTE_LCD)
+#if LCD_DEPTH >= 8 && (LCD_PIXELFORMAT == RGB565) && \
+ (LCD_STRIDEFORMAT == HORIZONTAL_STRIDE) && \
+ !defined(HAVE_LCD_SPLIT) && !defined(HAVE_REMOTE_LCD)
SDL_Rect src;
(void)max_x;
(void)max_y;
diff --git a/firmware/target/hosted/sdl/pcm-sdl.c b/firmware/target/hosted/sdl/pcm-sdl.c
index 7297e94340..940403002f 100644
--- a/firmware/target/hosted/sdl/pcm-sdl.c
+++ b/firmware/target/hosted/sdl/pcm-sdl.c
@@ -276,13 +276,6 @@ static void sdl_audio_callback(struct pcm_udata *udata, Uint8 *stream, int len)
SDL_UnlockMutex(audio_lock);
}
-const void * pcm_play_dma_get_peak_buffer(int *count)
-{
- uintptr_t addr = (uintptr_t)pcm_data;
- *count = pcm_data_size / 4;
- return (void *)((addr + 2) & ~3);
-}
-
#ifdef HAVE_RECORDING
void pcm_rec_lock(void)
{
diff --git a/firmware/target/hosted/sdl/sim-ui-defines.h b/firmware/target/hosted/sdl/sim-ui-defines.h
index c3ae7c0551..7d60deac34 100644
--- a/firmware/target/hosted/sdl/sim-ui-defines.h
+++ b/firmware/target/hosted/sdl/sim-ui-defines.h
@@ -203,13 +203,6 @@
#define UI_LCD_POSX 42 /* x position of lcd */
#define UI_LCD_POSY 35 /* y position of lcd */
-#elif defined(IRIVER_IFP7XX)
-#define UI_TITLE "iriver iFP7xx"
-#define UI_WIDTH 425 /* width of GUI window */
-#define UI_HEIGHT 183 /* height of GUI window */
-#define UI_LCD_POSX 115 /* x position of lcd */
-#define UI_LCD_POSY 54 /* y position of lcd */
-
#elif defined(MROBE_100)
#define UI_TITLE "Olympus M:Robe 100"
#define UI_WIDTH 247 /* width of GUI window */
@@ -226,13 +219,6 @@
#define UI_LCD_POSX 58 /* x position of lcd */
#define UI_LCD_POSY 67 /* y position of lcd */
-#elif defined(IAUDIO_7)
-#define UI_TITLE "iAudio7"
-#define UI_WIDTH 494 /* width of GUI window */
-#define UI_HEIGHT 214 /* height of GUI window */
-#define UI_LCD_POSX 131 /* x position of lcd */
-#define UI_LCD_POSY 38 /* y position of lcd */
-
#elif defined(CREATIVE_ZVM) || defined(CREATIVE_ZVM60GB)
#ifdef CREATIVE_ZVM
#define UI_TITLE "Creative Zen Vision:M 30GB"
@@ -528,7 +514,7 @@
#define UI_LCD_POSX 26
#define UI_LCD_POSY 40
-#elif defined(EROS_Q)
+#elif defined(EROS_Q) || defined(EROS_QN)
#define UI_TITLE "AIGO EROS Q"
#define UI_WIDTH 400 /* width of GUI window */
#define UI_HEIGHT 653 /* height of GUI window */
@@ -537,10 +523,18 @@
#elif defined(FIIO_M3K) || defined(FIIO_M3K_LINUX)
#define UI_TITLE "FiiO M3K"
-#define UI_WIDTH 287 /* width of GUI window */
-#define UI_HEIGHT 589 /* height of GUI window */
-#define UI_LCD_POSX 25
-#define UI_LCD_POSY 15
+#define UI_WIDTH 334 /* width of GUI window */
+#define UI_HEIGHT 684 /* height of GUI window */
+#define UI_LCD_POSX 49
+#define UI_LCD_POSY 37
+
+
+#elif defined(SHANLING_Q1)
+#define UI_TITLE "Shanling Q1"
+#define UI_WIDTH 466
+#define UI_HEIGHT 526
+#define UI_LCD_POSX 46
+#define UI_LCD_POSY 61
#elif defined(SIMULATOR)
diff --git a/firmware/target/hosted/sdl/thread-sdl.c b/firmware/target/hosted/sdl/thread-sdl.c
index a76941f103..17a0f847b5 100644
--- a/firmware/target/hosted/sdl/thread-sdl.c
+++ b/firmware/target/hosted/sdl/thread-sdl.c
@@ -215,7 +215,7 @@ void switch_thread(void)
} /* STATE_SLEEPING: */
}
-#ifdef DEBUG
+#ifdef BUFLIB_DEBUG_CHECK_VALID
core_check_valid();
#endif
__running_self_entry() = current;
diff --git a/firmware/target/hosted/system-hosted.c b/firmware/target/hosted/system-hosted.c
index a8b7dc82d3..c4ae5a404f 100644
--- a/firmware/target/hosted/system-hosted.c
+++ b/firmware/target/hosted/system-hosted.c
@@ -67,7 +67,7 @@ static void sig_handler(int sig, siginfo_t *siginfo, void *context)
lcd_putsf(0, line++, "%s at %08x", strsignal(sig), pc);
if(sig == SIGILL || sig == SIGFPE || sig == SIGSEGV || sig == SIGBUS || sig == SIGTRAP)
- lcd_putsf(0, line++, "address 0x%08x", siginfo->si_addr);
+ lcd_putsf(0, line++, "address %p", siginfo->si_addr);
if(!triggered)
{
@@ -90,6 +90,7 @@ static void sig_handler(int sig, siginfo_t *siginfo, void *context)
void power_off(void)
{
backlight_hw_off();
+ sync();
system("/sbin/poweroff");
while (1); /* halt */
}
diff --git a/firmware/target/hosted/usb-hiby.c b/firmware/target/hosted/usb-hiby.c
index b82fa5c4ce..050c86e446 100644
--- a/firmware/target/hosted/usb-hiby.c
+++ b/firmware/target/hosted/usb-hiby.c
@@ -63,8 +63,8 @@ void usb_enable(bool on)
*/
int disk_mount_all(void)
{
- const char *dev[] = {"/dev/mmcblk0p1", "/dev/mmcblk0"};
- const char *fs[] = {"vfat", "exfat"};
+ const char * const dev[] = {"/dev/mmcblk0p1", "/dev/mmcblk0"};
+ const char * const fs[] = {"vfat", "exfat"};
sysfs_set_string("/sys/class/android_usb/android0/f_mass_storage/lun/file", "");
diff --git a/firmware/target/hosted/xduoo/button-xduoo.c b/firmware/target/hosted/xduoo/button-xduoo.c
index 568ad2614d..c06b60bd9f 100644
--- a/firmware/target/hosted/xduoo/button-xduoo.c
+++ b/firmware/target/hosted/xduoo/button-xduoo.c
@@ -58,13 +58,13 @@ int button_map(int keycode)
#if defined(XDUOO_X3II) && defined(USE_REMOTE) /* Headphone remote */
case KEY_NEXTSONG:
- return (BUTTON_NEXT | BUTTON_DELAY_RELEASE);
+ return headphones_inserted()? (BUTTON_NEXT | BUTTON_DELAY_RELEASE) : 0;
case KEY_PLAYPAUSE:
- return (BUTTON_PLAY | BUTTON_DELAY_RELEASE);
+ return headphones_inserted()? (BUTTON_PLAY | BUTTON_DELAY_RELEASE) : 0;
case KEY_PREVIOUSSONG:
- return (BUTTON_PREV | BUTTON_DELAY_RELEASE);
+ return headphones_inserted()? (BUTTON_PREV | BUTTON_DELAY_RELEASE) : 0;
#endif
default:
diff --git a/firmware/target/mips/exception-mips.S b/firmware/target/mips/exception-mips.S
new file mode 100644
index 0000000000..605817c7d5
--- /dev/null
+++ b/firmware/target/mips/exception-mips.S
@@ -0,0 +1,181 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2022 Aidan MacDonald
+ *
+ * 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 "mips.h"
+
+ .text
+ .set push
+ .set mips32
+
+ .section .vectors.1, "ax", %progbits
+ la k1, tlb_refill_handler
+ j _exception_enter
+
+ .section .vectors.2, "ax", %progbits
+ la k1, cache_error_handler
+ j _exception_enter
+
+ .section .vectors.3, "ax", %progbits
+ li k1, M_CauseExcCode
+ mfc0 k0, C0_CAUSE
+ and k0, k1
+ beq zero, k0, 1f
+ la k1, exception_handler
+ j _exception_enter
+1:
+ la k1, intr_handler
+ j _exception_enter
+
+ .section .vectors.4, "ax", %progbits
+ /* same as above */
+ li k1, M_CauseExcCode
+ mfc0 k0, C0_CAUSE
+ and k0, k1
+ beq zero, k0, 1f
+ nop
+ la k1, exception_handler
+ j _exception_enter
+1:
+ la k1, intr_handler
+ j _exception_enter
+
+ .set push
+ .set noat
+ .set noreorder
+ .section .vectors, "ax", %progbits
+
+_exception_enter:
+ la k0, _irqstackend
+ addiu k0, -0x84
+
+ sw $1, 0x00(k0)
+ sw $2, 0x04(k0)
+ sw $3, 0x08(k0)
+ sw $4, 0x0c(k0)
+ sw $5, 0x10(k0)
+ sw $6, 0x14(k0)
+ sw $7, 0x18(k0)
+ sw $8, 0x1c(k0)
+ sw $9, 0x20(k0)
+ sw $10, 0x24(k0)
+ sw $11, 0x28(k0)
+ sw $12, 0x2c(k0)
+ sw $13, 0x30(k0)
+ sw $14, 0x34(k0)
+ sw $15, 0x38(k0)
+ sw $16, 0x3c(k0)
+ sw $17, 0x40(k0)
+ sw $18, 0x44(k0)
+ sw $19, 0x48(k0)
+ sw $20, 0x4c(k0)
+ sw $21, 0x50(k0)
+ sw $22, 0x54(k0)
+ sw $23, 0x58(k0)
+ sw $24, 0x5c(k0)
+ sw $25, 0x60(k0)
+ sw $28, 0x64(k0)
+ sw $29, 0x68(k0)
+ sw $30, 0x6c(k0)
+ sw $31, 0x70(k0)
+
+ mflo t0
+ ssnop
+ sw t0, 0x74(k0)
+
+ mfhi t0
+ ssnop
+ sw t0, 0x78(k0)
+
+ mfc0 t0, C0_STATUS
+ ssnop
+ ssnop
+ ssnop
+ sw t0, 0x7c(k0)
+
+ mfc0 a1, C0_EPC
+ ssnop
+ ssnop
+ ssnop
+ sw a1, 0x80(k0)
+
+ move sp, k0
+
+ jalr k1
+ move a0, sp
+
+_exception_return:
+ /* restore all GPRs except sp */
+ lw $1, 0x00(sp)
+ lw $2, 0x04(sp)
+ lw $3, 0x08(sp)
+ lw $4, 0x0c(sp)
+ lw $5, 0x10(sp)
+ lw $6, 0x14(sp)
+ lw $7, 0x18(sp)
+ lw $8, 0x1c(sp)
+ lw $9, 0x20(sp)
+ lw $10, 0x24(sp)
+ lw $11, 0x28(sp)
+ lw $12, 0x2c(sp)
+ lw $13, 0x30(sp)
+ lw $14, 0x34(sp)
+ lw $15, 0x38(sp)
+ lw $16, 0x3c(sp)
+ lw $17, 0x40(sp)
+ lw $18, 0x44(sp)
+ lw $19, 0x48(sp)
+ lw $20, 0x4c(sp)
+ lw $21, 0x50(sp)
+ lw $22, 0x54(sp)
+ lw $23, 0x58(sp)
+ lw $24, 0x5c(sp)
+ lw $25, 0x60(sp)
+ lw $28, 0x64(sp)
+ lw $30, 0x6c(sp)
+ lw $31, 0x70(sp)
+
+ lw k0, 0x74(sp)
+ mtlo k0
+ ssnop
+
+ lw k0, 0x78(sp)
+ mthi k0
+ ssnop
+
+ lw k0, 0x7c(sp)
+ mtc0 k0, C0_STATUS
+ ssnop
+ ssnop
+ ssnop
+
+ lw k0, 0x80(sp)
+ mtc0 k0, C0_EPC
+ ssnop
+ ssnop
+ ssnop
+
+ /* restore stack pointer last */
+ lw sp, 0x68(sp)
+ eret
+ ssnop
+
+ .set pop
+ .set pop
diff --git a/firmware/target/mips/ingenic_jz47xx/app.lds b/firmware/target/mips/ingenic_jz47xx/app.lds
index bec8671ec9..1d300fed82 100644
--- a/firmware/target/mips/ingenic_jz47xx/app.lds
+++ b/firmware/target/mips/ingenic_jz47xx/app.lds
@@ -1,9 +1,13 @@
#include "config.h"
+#define __ASSEMBLY__
+#include "cpu.h"
OUTPUT_FORMAT("elf32-littlemips")
OUTPUT_ARCH(MIPS)
ENTRY(_start)
STARTUP(target/mips/ingenic_jz47xx/crt0.o)
+INPUT(target/mips/exception-mips.o)
+INPUT(target/mips/system-mips.o)
#define STUBOFFSET 0x4000
@@ -18,22 +22,34 @@ STARTUP(target/mips/ingenic_jz47xx/crt0.o)
/* Where the codec buffer ends, and the plugin buffer starts */
#define ENDCODECADDR (ENDAUDIOADDR + CODEC_SIZE)
+/* Place init code in the codec buffer */
+#define INITBASE ENDAUDIOADDR
+#define INITSIZE CODEC_SIZE
+
MEMORY
{
DRAM : ORIGIN = DRAMORIG, LENGTH = DRAMSIZE
IRAM : ORIGIN = IRAMORIG, LENGTH = IRAMSIZE
+ INIT : ORIGIN = INITBASE, LENGTH = INITSIZE
}
SECTIONS
{
. = DRAMORIG;
- .text :
+ .startup :
{
loadaddress = .;
_loadaddress = .;
- *(.init.text);
+ *(.startup.text);
+ } > DRAM
+
+ .text :
+ {
*(.text*);
+#ifndef HAVE_INIT_ATTR
+ *(.init*);
+#endif
} > DRAM
. = ALIGN(4);
@@ -66,7 +82,7 @@ SECTIONS
KEEP(*(.vectors.4));
KEEP(*(.vectors));
- *(.icode);
+ *(.icode*);
*(.irodata);
*(.idata);
KEEP(*(.vectors))
@@ -75,6 +91,16 @@ SECTIONS
} > IRAM
_iramcopy = LOADADDR(.iram);
+#ifdef HAVE_INIT_ATTR
+ .init :
+ {
+ _initstart = .;
+ *(.init*);
+ _initend = .;
+ } > INIT AT> DRAM
+ _initcopy = LOADADDR(.init);
+#endif
+
. = ALIGN(4);
.stack (NOLOAD):
@@ -83,9 +109,9 @@ SECTIONS
stackbegin = .;
. += 0x1d00;
stackend = .;
- irqstackbegin = .;
+ _irqstackbegin = .;
. += 0x400;
- irqstackend = .;
+ _irqstackend = .;
} > IRAM
.bss (NOLOAD):
@@ -111,6 +137,7 @@ SECTIONS
/DISCARD/ :
{
+ *(.MIPS.abiflags);
*(.eh_frame);
*(.rel.dyn);
}
diff --git a/firmware/target/mips/ingenic_jz47xx/boot.lds b/firmware/target/mips/ingenic_jz47xx/boot.lds
index 9492050695..b1ea5ddf13 100644
--- a/firmware/target/mips/ingenic_jz47xx/boot.lds
+++ b/firmware/target/mips/ingenic_jz47xx/boot.lds
@@ -4,6 +4,8 @@ OUTPUT_FORMAT("elf32-littlemips")
OUTPUT_ARCH(MIPS)
ENTRY(_start)
STARTUP(target/mips/ingenic_jz47xx/crt0.o)
+INPUT(target/mips/exception-mips.o)
+INPUT(target/mips/system-mips.o)
#define DRAMSIZE ((MEMORYSIZE-4) * 0x100000)
@@ -58,7 +60,7 @@ SECTIONS
KEEP(*(.vectors.4));
KEEP(*(.vectors));
- *(.icode);
+ *(.icode*);
*(.irodata);
*(.idata);
KEEP(*(.vectors*))
@@ -87,8 +89,15 @@ SECTIONS
stackbegin = .;
. += 0x1d00;
stackend = .;
- irqstackbegin = .;
+ _irqstackbegin = .;
. += 0x400;
- irqstackend = .;
+ _irqstackend = .;
} > IRAM
+
+ /DISCARD/ :
+ {
+ *(.MIPS.abiflags);
+ *(.eh_frame);
+ *(.rel.dyn);
+ }
}
diff --git a/firmware/target/mips/ingenic_jz47xx/crt0.S b/firmware/target/mips/ingenic_jz47xx/crt0.S
index 258a5c02fc..b73a43d8f2 100644
--- a/firmware/target/mips/ingenic_jz47xx/crt0.S
+++ b/firmware/target/mips/ingenic_jz47xx/crt0.S
@@ -40,11 +40,12 @@
.text
- .extern system_main
+ .extern system_early_init
.extern main
.global _start
- .section .init.text
+ .section .startup.text,"ax",%progbits
+ .set push
.set mips32
.set noreorder
.set noat
@@ -138,6 +139,19 @@ _iram_loop:
bne t1, t2, _iram_loop
sw t3, -4(t1)
+#ifdef HAVE_INIT_ATTR
+ /* Copy init code */
+ la t0, _initcopy
+ la t1, _initstart
+ la t2, _initend
+_init_loop:
+ lw t3, 0(t0)
+ addiu t1, 4
+ addiu t0, 4
+ bne t1, t2, _init_loop
+ sw t3, -4(t1)
+#endif
+
/*
----------------------------------------------------
Clear BSS section
@@ -169,8 +183,8 @@ _stack_loop:
Set up interrupt stack
----------------------------------------------------
*/
- la k0, irqstackend
- la t0, irqstackbegin
+ la k0, _irqstackend
+ la t0, _irqstackbegin
_irq_stack_loop:
addiu t0, 4
@@ -182,167 +196,9 @@ _irq_stack_loop:
Jump to C code
----------------------------------------------------
*/
- jal system_main /* Init clocks etc first */
+ jal system_early_init /* Init clocks etc first */
ssnop
j main
- ssnop
-
-
- /*
- * 0x0 - Simple TLB refill handler
- * 0x100 - Cache error handler
- * 0x180 - Exception/Interrupt handler
- * 0x200 - Special Exception Interrupt handler (when IV is set in CP0_CAUSE)
- */
-
- .section .vectors.1, "ax", %progbits
- j tlb_refill_handler
- ssnop
-
- .section .vectors.2, "ax", %progbits
- j real_exception_handler
- ssnop
-
- .section .vectors.3, "ax", %progbits
- j real_exception_handler
- ssnop
-
- .section .vectors.4, "ax", %progbits
- j real_exception_handler
- ssnop
-
- .section .vectors, "ax", %progbits
-real_exception_handler:
-
- /* Store stack pointer */
- move k0, sp
- /* jump to IRQ stack */
- la sp, irqstackend
-
- /* Push crap on frame */
- addiu sp, -0x84
- /* store current stack pointer */
- sw k0, 0x80(sp)
-
- sw ra, 0(sp)
- sw fp, 4(sp)
- sw gp, 8(sp)
- sw t9, 0xC(sp)
- sw t8, 0x10(sp)
- sw s7, 0x14(sp)
- sw s6, 0x18(sp)
- sw s5, 0x1C(sp)
- sw s4, 0x20(sp)
- sw s3, 0x24(sp)
- sw s2, 0x28(sp)
- sw s1, 0x2C(sp)
- sw s0, 0x30(sp)
- sw t7, 0x34(sp)
- sw t6, 0x38(sp)
- sw t5, 0x3C(sp)
- sw t4, 0x40(sp)
- sw t3, 0x44(sp)
- sw t2, 0x48(sp)
- sw t1, 0x4C(sp)
- sw t0, 0x50(sp)
- sw a3, 0x54(sp)
- sw a2, 0x58(sp)
- sw a1, 0x5C(sp)
- sw a0, 0x60(sp)
- sw v1, 0x64(sp)
- sw v0, 0x68(sp)
- sw $1, 0x6C(sp)
- mflo k0
- ssnop
- sw k0, 0x70(sp)
- mfhi k0
- ssnop
- sw k0, 0x74(sp)
- mfc0 k0, C0_STATUS
- ssnop
- ssnop
- ssnop
- sw k0, 0x78(sp)
- mfc0 k0, C0_EPC
- ssnop
- ssnop
- ssnop
- sw k0, 0x7C(sp)
-
- li k1, M_CauseExcCode
- mfc0 k0, C0_CAUSE
- and k0, k1
- beq zero, k0, _int
- ssnop
- j _exception
- ssnop
-
-_int:
- jal intr_handler
- ssnop
- j _exception_return
-
-_exception:
- move a0, sp
- mfc0 a1, C0_CAUSE
- ssnop
- ssnop
- ssnop
- mfc0 a2, C0_EPC
- ssnop
- ssnop
- ssnop
- jal exception_handler
- ssnop
+ move ra, zero /* init backtrace root */
-_exception_return:
- lw ra, 0(sp)
- lw fp, 4(sp)
- lw gp, 8(sp)
- lw t9, 0xC(sp)
- lw t8, 0x10(sp)
- lw s7, 0x14(sp)
- lw s6, 0x18(sp)
- lw s5, 0x1C(sp)
- lw s4, 0x20(sp)
- lw s3, 0x24(sp)
- lw s2, 0x28(sp)
- lw s1, 0x2C(sp)
- lw s0, 0x30(sp)
- lw t7, 0x34(sp)
- lw t6, 0x38(sp)
- lw t5, 0x3C(sp)
- lw t4, 0x40(sp)
- lw t3, 0x44(sp)
- lw t2, 0x48(sp)
- lw t1, 0x4C(sp)
- lw t0, 0x50(sp)
- lw a3, 0x54(sp)
- lw a2, 0x58(sp)
- lw a1, 0x5C(sp)
- lw a0, 0x60(sp)
- lw v1, 0x64(sp)
- lw v0, 0x68(sp)
- lw $1, 0x6C(sp)
- lw k0, 0x70(sp)
- mtlo k0
- ssnop
- lw k0, 0x74(sp)
- mthi k0
- ssnop
- lw k0, 0x78(sp)
- mtc0 k0, C0_STATUS
- ssnop
- ssnop
- ssnop
- lw k0, 0x7C(sp)
- mtc0 k0, C0_EPC
- ssnop
- ssnop
- ssnop
- /* Restore previous stack pointer */
- lw sp, 0x80(sp)
- eret
- ssnop
- .set reorder
- .set at
+ .set pop
diff --git a/firmware/target/mips/ingenic_jz47xx/debug-jz4760.c b/firmware/target/mips/ingenic_jz47xx/debug-jz4760.c
index 65b5e93887..f5624cf469 100644
--- a/firmware/target/mips/ingenic_jz47xx/debug-jz4760.c
+++ b/firmware/target/mips/ingenic_jz47xx/debug-jz4760.c
@@ -537,7 +537,7 @@ static const char* hw_info_menu_get_name(int item, void * data,
(void)data;
struct tm *cur_time;
uint32_t *stackptr;
- extern uint32_t irqstackend,irqstackbegin;
+ extern uint32_t _irqstackend,_irqstackbegin;
int btn;
#ifdef HAVE_TOUCHSCREEN
int touch;
@@ -557,10 +557,10 @@ static const char* hw_info_menu_get_name(int item, void * data,
read_cp0_15(), (REG_CPM_CLKGR0 & BIT31) >> 31);
return buffer;
case 2: /*IRQstack*/
- stackptr = &irqstackbegin;
- for ( ; stackptr < &irqstackend && *stackptr == 0xDEADBEEF; stackptr++) {}
+ stackptr = &_irqstackbegin;
+ for ( ; stackptr < &_irqstackend && *stackptr == 0xDEADBEEF; stackptr++) {}
snprintf(buffer, buffer_len, "IRQ stack max: %lu",
- (uint32_t)&irqstackend - (uint32_t)stackptr);
+ (uint32_t)&_irqstackend - (uint32_t)stackptr);
return buffer;
case 5: /*Touch/BTN*/
#ifdef HAVE_TOUCHSCREEN
diff --git a/firmware/target/mips/ingenic_jz47xx/pcm-jz4740.c b/firmware/target/mips/ingenic_jz47xx/pcm-jz4740.c
index 83325e09ef..178da0b153 100644
--- a/firmware/target/mips/ingenic_jz47xx/pcm-jz4740.c
+++ b/firmware/target/mips/ingenic_jz47xx/pcm-jz4740.c
@@ -184,47 +184,6 @@ void pcm_play_unlock(void)
restore_irq(flags);
}
-static int get_dma_count(void)
-{
- int count = REG_DMAC_DTCR(DMA_AIC_TX_CHANNEL);
- switch(REG_DMAC_DCMD(DMA_AIC_TX_CHANNEL) & DMAC_DCMD_DS_MASK)
- {
- case DMAC_DCMD_DS_16BIT:
- count *= 2;
- break;
- case DMAC_DCMD_DS_32BIT:
- count *= 4;
- break;
- case DMAC_DCMD_DS_16BYTE:
- count *= 16;
- break;
- }
-
- return count;
-}
-
-const void * pcm_play_dma_get_peak_buffer(int *count)
-{
- int flags = disable_irq_save();
-
- const void* addr;
- if(REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) & DMAC_DCCSR_EN)
- {
- int bytes = get_dma_count();
- *count = bytes >> 2;
- addr = (const void*)((int)(playback_address + bytes + 2) & ~3);
- }
- else
- {
- *count = 0;
- addr = NULL;
- }
-
- restore_irq(flags);
-
- return addr;
-}
-
void audiohw_close(void)
{
/* TODO: prevent pop */
diff --git a/firmware/target/mips/ingenic_jz47xx/pcm-jz4760.c b/firmware/target/mips/ingenic_jz47xx/pcm-jz4760.c
index 401d568a97..72bcbeccd0 100644
--- a/firmware/target/mips/ingenic_jz47xx/pcm-jz4760.c
+++ b/firmware/target/mips/ingenic_jz47xx/pcm-jz4760.c
@@ -172,44 +172,3 @@ void pcm_play_unlock(void)
restore_irq(flags);
}
-
-static int get_dma_count(void)
-{
- int count = REG_DMAC_DTCR(DMA_AIC_TX_CHANNEL);
- switch(REG_DMAC_DCMD(DMA_AIC_TX_CHANNEL) & DMAC_DCMD_DS_MASK)
- {
- case DMAC_DCMD_DS_16BIT:
- count *= 2;
- break;
- case DMAC_DCMD_DS_32BIT:
- count *= 4;
- break;
- case DMAC_DCMD_DS_16BYTE:
- count *= 16;
- break;
- }
-
- return count;
-}
-
-const void * pcm_play_dma_get_peak_buffer(int *count)
-{
- int flags = disable_irq_save();
-
- const void* addr;
- if(REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) & DMAC_DCCSR_EN)
- {
- int bytes = get_dma_count();
- *count = bytes >> 2;
- addr = (const void*)((int)(playback_address + bytes + 2) & ~3);
- }
- else
- {
- *count = 0;
- addr = NULL;
- }
-
- restore_irq(flags);
-
- return addr;
-}
diff --git a/firmware/target/mips/ingenic_jz47xx/system-jz4740.c b/firmware/target/mips/ingenic_jz47xx/system-jz4740.c
index fdc335ad21..5aff144327 100644
--- a/firmware/target/mips/ingenic_jz47xx/system-jz4740.c
+++ b/firmware/target/mips/ingenic_jz47xx/system-jz4740.c
@@ -219,80 +219,6 @@ void intr_handler(void)
irqvector[irq-1]();
}
-#define EXC(x,y) case (x): return (y);
-static char* parse_exception(unsigned int cause)
-{
- switch(cause & M_CauseExcCode)
- {
- EXC(EXC_INT, "Interrupt");
- EXC(EXC_MOD, "TLB Modified");
- EXC(EXC_TLBL, "TLB Exception (Load or Ifetch)");
- EXC(EXC_ADEL, "Address Error (Load or Ifetch)");
- EXC(EXC_ADES, "Address Error (Store)");
- EXC(EXC_TLBS, "TLB Exception (Store)");
- EXC(EXC_IBE, "Instruction Bus Error");
- EXC(EXC_DBE, "Data Bus Error");
- EXC(EXC_SYS, "Syscall");
- EXC(EXC_BP, "Breakpoint");
- EXC(EXC_RI, "Reserved Instruction");
- EXC(EXC_CPU, "Coprocessor Unusable");
- EXC(EXC_OV, "Overflow");
- EXC(EXC_TR, "Trap Instruction");
- EXC(EXC_FPE, "Floating Point Exception");
- EXC(EXC_C2E, "COP2 Exception");
- EXC(EXC_MDMX, "MDMX Exception");
- EXC(EXC_WATCH, "Watch Exception");
- EXC(EXC_MCHECK, "Machine Check Exception");
- EXC(EXC_CacheErr, "Cache error caused re-entry to Debug Mode");
- default:
- return NULL;
- }
-}
-
-void exception_handler(void* stack_ptr, unsigned int cause, unsigned int epc)
-{
-#if EXTENDED_EXCEPTION_DESC
- (void)epc;
-
- /* Depends on crt0.S ! */
- char *registers[] = { "ra", "fp", "gp", "t9", "t8", "s7", "s6", "s5", "s4",
- "s3", "s2", "s1", "s0", "t7", "t6", "t5", "t4", "t3",
- "t2", "t1", "t0", "a3", "a2", "a1", "a0", "v1", "v0",
- "$1", "LO", "HI", "STATUS", "EPC" };
- int i;
-
-#if LCD_DEPTH > 1
- lcd_set_backdrop(NULL);
- lcd_set_drawmode(DRMODE_SOLID);
- lcd_set_foreground(LCD_BLACK);
- lcd_set_background(LCD_WHITE);
-#endif
- lcd_setfont(FONT_SYSFIXED);
- lcd_set_viewport(NULL);
-
- lcd_clear_display();
- backlight_hw_on();
-
- lcd_puts(0, 0, parse_exception(cause));
- lcd_putsf(0, 1, "0x%08x at 0x%08x", read_c0_badvaddr(), epc);
- for(i=0; i< 0x80/4; i+=2)
- {
- unsigned int* ptr = (unsigned int*)(stack_ptr + i*4);
- lcd_putsf(0, 3 + i/2, "%s: 0x%08x %s: 0x%08x", registers[i], *ptr, registers[i+1], *(ptr+1));
- }
- lcd_update();
-
- system_exception_wait();
-#else
- panicf("Exception occurred: %s [0x%08x] at 0x%08x (stack at 0x%08x)", parse_exception(cause), read_c0_badvaddr(), epc, (unsigned int)stack_ptr);
-#endif
-}
-
-void tlb_refill_handler(void)
-{
- panicf("TLB refill handler at 0x%08lx! [0x%x]", read_c0_epc(), read_c0_badvaddr());
-}
-
void udelay(unsigned int usec)
{
unsigned int i = usec * (__cpm_get_cclk() / 2000000);
@@ -508,7 +434,7 @@ static void sdram_init(void)
}
/* Gets called *before* main */
-void ICODE_ATTR system_main(void)
+void ICODE_ATTR system_early_init(void)
{
int i;
diff --git a/firmware/target/mips/ingenic_jz47xx/system-jz4760.c b/firmware/target/mips/ingenic_jz47xx/system-jz4760.c
index 10973f3e2b..3a1321b992 100644
--- a/firmware/target/mips/ingenic_jz47xx/system-jz4760.c
+++ b/firmware/target/mips/ingenic_jz47xx/system-jz4760.c
@@ -301,46 +301,6 @@ top:
goto top;
}
-#define EXC(x,y) case (x): return (y);
-static char* parse_exception(unsigned int cause)
-{
- switch(cause & M_CauseExcCode)
- {
- EXC(EXC_INT, "Interrupt");
- EXC(EXC_MOD, "TLB Modified");
- EXC(EXC_TLBL, "TLB Exception (Load or Ifetch)");
- EXC(EXC_ADEL, "Address Error (Load or Ifetch)");
- EXC(EXC_ADES, "Address Error (Store)");
- EXC(EXC_TLBS, "TLB Exception (Store)");
- EXC(EXC_IBE, "Instruction Bus Error");
- EXC(EXC_DBE, "Data Bus Error");
- EXC(EXC_SYS, "Syscall");
- EXC(EXC_BP, "Breakpoint");
- EXC(EXC_RI, "Reserved Instruction");
- EXC(EXC_CPU, "Coprocessor Unusable");
- EXC(EXC_OV, "Overflow");
- EXC(EXC_TR, "Trap Instruction");
- EXC(EXC_FPE, "Floating Point Exception");
- EXC(EXC_C2E, "COP2 Exception");
- EXC(EXC_MDMX, "MDMX Exception");
- EXC(EXC_WATCH, "Watch Exception");
- EXC(EXC_MCHECK, "Machine Check Exception");
- EXC(EXC_CacheErr, "Cache error caused re-entry to Debug Mode");
- default:
- return NULL;
- }
-}
-
-void exception_handler(void* stack_ptr, unsigned int cause, unsigned int epc)
-{
- panicf("Exception occurred: %s [0x%08x] at 0x%08x (stack at 0x%08x)", parse_exception(cause), read_c0_badvaddr(), epc, (unsigned int)stack_ptr);
-}
-
-void tlb_refill_handler(void)
-{
- panicf("TLB refill handler at 0x%08lx! [0x%x]", read_c0_epc(), read_c0_badvaddr());
-}
-
#ifdef HW_UDELAY_TIMER
/* This enables the HW timer, set to EXT_XTAL / 4 (so @ 12/4 = 3MHz, 1 us = 3 ticks) */
static void init_delaytimer(void)
@@ -573,7 +533,7 @@ static void serial_setbrg(void)
*uart_lcr = tmp;
}
-int serial_preinit(void)
+static int serial_preinit(void)
{
volatile u8 *uart_fcr = (volatile u8 *)(CFG_UART_BASE + OFF_FCR);
volatile u8 *uart_lcr = (volatile u8 *)(CFG_UART_BASE + OFF_LCR);
@@ -609,7 +569,7 @@ int serial_preinit(void)
#define cpu_frequency CPU_FREQ
#endif
-void usb_preinit(void)
+static void usb_preinit(void)
{
/* Clear ECS bit of CPCCR, 0:clock source is EXCLK, 1:clock source is EXCLK/2 */
REG_CPM_CPCCR &= ~CPCCR_ECS;
@@ -661,7 +621,7 @@ void usb_preinit(void)
udelay(300);
}
-void dma_preinit(void)
+static void dma_preinit(void)
{
__cpm_start_mdma();
__cpm_start_dmac();
@@ -676,7 +636,7 @@ void dma_preinit(void)
}
/* Gets called *before* main */
-void ICODE_ATTR system_main(void)
+void ICODE_ATTR system_early_init(void)
{
int i;
diff --git a/firmware/target/mips/ingenic_jz47xx/system-target.h b/firmware/target/mips/ingenic_jz47xx/system-target.h
index 2725944452..1db0ee4671 100644
--- a/firmware/target/mips/ingenic_jz47xx/system-target.h
+++ b/firmware/target/mips/ingenic_jz47xx/system-target.h
@@ -27,6 +27,7 @@
#include "config.h"
#include "cpu.h"
#include "mipsregs.h"
+#include "system-mips.h"
#define CACHE_SIZE 16*1024
#define CACHEALIGN_BITS 5
diff --git a/firmware/target/mips/ingenic_jz47xx/usb-jz4740.c b/firmware/target/mips/ingenic_jz47xx/usb-jz4740.c
index 162aeec97a..07697da723 100644
--- a/firmware/target/mips/ingenic_jz47xx/usb-jz4740.c
+++ b/firmware/target/mips/ingenic_jz47xx/usb-jz4740.c
@@ -75,7 +75,7 @@ static unsigned char ep0_rx_buf[64];
static struct usb_endpoint endpoints[] =
{
{ .type = ep_control, .fifo_addr = USB_FIFO_EP0, .fifo_size = 64 },
- { .type = ep_control, .fifo_addr = USB_FIFO_EP0, .buf = &ep0_rx_buf },
+ { .type = ep_control, .fifo_addr = USB_FIFO_EP0, .buf = ep0_rx_buf },
{ .type = ep_bulk, .fifo_addr = USB_FIFO_EP1, .fifo_size = 512 },
{ .type = ep_bulk, .fifo_addr = USB_FIFO_EP1, .fifo_size = 512 },
{ .type = ep_interrupt, .fifo_addr = USB_FIFO_EP2, .fifo_size = 64 },
@@ -193,7 +193,7 @@ static void EP0_send(void)
if(ep->sent >= ep->length)
{
REG_USB_REG_CSR0 = (csr0 | USB_CSR0_INPKTRDY | USB_CSR0_DATAEND); /* Set data end! */
- usb_core_transfer_complete(0, USB_DIR_IN, 0, ep->sent);
+ usb_core_transfer_complete(EP_CONTROL, USB_DIR_IN, 0, ep->sent);
ep_transfer_completed(ep);
}
else
@@ -239,7 +239,7 @@ static void EP0_handler(void)
{
readFIFO(ep_recv, REG_USB_REG_COUNT0);
REG_USB_REG_CSR0 = csr0 | USB_CSR0_SVDOUTPKTRDY; /* clear OUTPKTRDY bit */
- usb_core_control_request((struct usb_ctrlrequest*)ep_recv->buf);
+ usb_core_legacy_control_request((struct usb_ctrlrequest*)ep_recv->buf);
}
}
@@ -743,7 +743,7 @@ int usb_drv_send(int endpoint, void* ptr, int length)
return 0;
}
-int usb_drv_recv(int endpoint, void* ptr, int length)
+int usb_drv_recv_nonblocking(int endpoint, void* ptr, int length)
{
int flags;
struct usb_endpoint *ep;
diff --git a/firmware/target/mips/ingenic_jz47xx/usb-jz4760.c b/firmware/target/mips/ingenic_jz47xx/usb-jz4760.c
index 37df1b3bb6..8ff6d4bc1e 100644
--- a/firmware/target/mips/ingenic_jz47xx/usb-jz4760.c
+++ b/firmware/target/mips/ingenic_jz47xx/usb-jz4760.c
@@ -54,16 +54,14 @@
OUT = HOST->DEV, (ie we recv)
*/
-enum ep_type
-{
+enum ep_type {
ep_control,
ep_bulk,
ep_interrupt,
ep_isochronous
};
-struct usb_endpoint
-{
+struct usb_endpoint {
const enum ep_type type;
const long fifo_addr;
unsigned short fifo_size;
@@ -76,8 +74,7 @@ struct usb_endpoint
volatile void *buf;
volatile size_t length;
- union
- {
+ union {
volatile size_t sent;
volatile size_t received;
};
@@ -94,8 +91,7 @@ struct usb_endpoint
#define short_not_ok 1 /* only works for mass storage.. */
#define ep_doublebuf(__ep) 0
-static union
-{
+static union {
int buf[64 / sizeof(int)];
struct usb_ctrlrequest request;
} ep0_rx;
@@ -106,7 +102,7 @@ static volatile bool ep0_data_requested = false;
static struct usb_endpoint endpoints[] =
{
EP_INIT(ep_control, USB_FIFO_EP(0), 64, NULL),
- EP_INIT(ep_control, USB_FIFO_EP(0), 64, &ep0_rx.buf),
+ EP_INIT(ep_control, USB_FIFO_EP(0), 64, NULL),
EP_INIT(ep_bulk, USB_FIFO_EP(1), 512, NULL),
EP_INIT(ep_bulk, USB_FIFO_EP(1), 512, NULL),
EP_INIT(ep_interrupt, USB_FIFO_EP(2), 512, NULL),
@@ -127,19 +123,14 @@ static void readFIFO(struct usb_endpoint *ep, unsigned int size)
register unsigned int s = size >> 2;
register unsigned int x;
- if(size > 0)
- {
- if( ((unsigned int)ptr & 3) == 0 )
- {
- while(s--)
+ if(size > 0) {
+ if (((unsigned int)ptr & 3) == 0) {
+ while(s--) {
*ptr32++ = REG32(ep->fifo_addr);
-
+ }
ptr = (unsigned char*)ptr32;
- }
- else
- {
- while(s--)
- {
+ } else {
+ while(s--) {
x = REG32(ep->fifo_addr);
*ptr++ = x & 0xFF; x >>= 8;
*ptr++ = x & 0xFF; x >>= 8;
@@ -147,10 +138,10 @@ static void readFIFO(struct usb_endpoint *ep, unsigned int size)
*ptr++ = x;
}
}
-
s = size & 3;
- while(s--)
+ while(s--) {
*ptr++ = REG8(ep->fifo_addr);
+ }
}
}
@@ -163,18 +154,14 @@ static void writeFIFO(struct usb_endpoint *ep, size_t size)
register size_t s = size >> 2;
register unsigned int x;
- if(size > 0)
- {
- if( ((unsigned int)d8 & 3) == 0 )
- {
- while (s--)
+ if (size > 0) {
+ if (((unsigned int)d8 & 3) == 0) {
+ while (s--) {
REG32(ep->fifo_addr) = *d32++;
+ }
d8 = (unsigned char *)d32;
- }
- else
- {
- while (s--)
- {
+ } else {
+ while (s--) {
x = (unsigned int)(*d8++) & 0xff;
x |= ((unsigned int)(*d8++) & 0xff) << 8;
x |= ((unsigned int)(*d8++) & 0xff) << 16;
@@ -182,11 +169,9 @@ static void writeFIFO(struct usb_endpoint *ep, size_t size)
REG32(ep->fifo_addr) = x;
}
}
-
- if( (s = size & 3) )
- {
- while (s--)
- REG8(ep->fifo_addr) = *d8++;
+ s = size & 3;
+ while (s--) {
+ REG8(ep->fifo_addr) = *d8++;
}
}
}
@@ -195,18 +180,17 @@ static void flushFIFO(struct usb_endpoint *ep)
{
logf("%s(%d)", __func__, EP_NUMBER(ep));
- switch (ep->type)
- {
- case ep_control:
+ switch (ep->type) {
+ case ep_control:
break;
-
- case ep_bulk:
- case ep_interrupt:
- case ep_isochronous:
- if(EP_IS_IN(ep))
- REG_USB_INCSR |= (USB_INCSR_FF | USB_INCSR_CDT);
- else
- REG_USB_OUTCSR |= (USB_OUTCSR_FF | USB_OUTCSR_CDT);
+ case ep_bulk:
+ case ep_interrupt:
+ case ep_isochronous:
+ if (EP_IS_IN(ep)) {
+ REG_USB_INCSR |= (USB_INCSR_FF | USB_INCSR_CDT);
+ } else {
+ REG_USB_OUTCSR |= (USB_OUTCSR_FF | USB_OUTCSR_CDT);
+ }
break;
}
}
@@ -217,8 +201,9 @@ static inline void ep_transfer_completed(struct usb_endpoint* ep)
ep->length = 0;
ep->buf = NULL;
ep->busy = false;
- if(ep->wait)
+ if (ep->wait) {
semaphore_release(&ep->complete);
+ }
}
static void EP0_send(void)
@@ -230,27 +215,28 @@ static void EP0_send(void)
select_endpoint(0);
csr0 = REG_USB_CSR0;
- if(ep->sent == 0)
- {
+ logf("%s(): 0x%x %d %d", __func__, csr0, ep->sent, ep->length);
+
+ if (ep->sent == 0) {
length = MIN(ep->length, ep->fifo_size);
REG_USB_CSR0 = (csr0 | USB_CSR0_FLUSHFIFO);
- }
- else
+ } else {
length = MIN(EP_BUF_LEFT(ep), ep->fifo_size);
+ }
writeFIFO(ep, length);
ep->sent += length;
- if(ep->sent >= ep->length)
- {
+ if (ep->sent >= ep->length) {
REG_USB_CSR0 = (csr0 | USB_CSR0_INPKTRDY | USB_CSR0_DATAEND); /* Set data end! */
- if (!ep->wait)
- usb_core_transfer_complete(0, USB_DIR_IN, 0, ep->sent);
+ if (!ep->wait) {
+ usb_core_transfer_complete(EP_CONTROL, USB_DIR_IN, 0, ep->sent);
+ }
ep->rc = 0;
ep_transfer_completed(ep);
- }
- else
+ } else {
REG_USB_CSR0 = (csr0 | USB_CSR0_INPKTRDY);
+ }
}
static void EP0_handler(void)
@@ -263,13 +249,12 @@ static void EP0_handler(void)
select_endpoint(0);
csr0 = REG_USB_CSR0;
- logf("%s(): 0x%x", __func__, csr0);
+ logf("%s(): 0x%x %d", __func__, csr0, ep_send->busy);
/* Check for SentStall:
This bit is set when a STALL handshake is transmitted. The CPU should clear this bit.
*/
- if(csr0 & USB_CSR0_SENTSTALL)
- {
+ if (csr0 & USB_CSR0_SENTSTALL) {
REG_USB_CSR0 = csr0 & ~USB_CSR0_SENTSTALL;
return;
}
@@ -279,68 +264,62 @@ static void EP0_handler(void)
An interrupt will be generated and the FIFO flushed at this time.
The bit is cleared by the CPU writing a 1 to the ServicedSetupEnd bit.
*/
- if(csr0 & USB_CSR0_SETUPEND)
- {
+ if (csr0 & USB_CSR0_SETUPEND) {
csr0 |= USB_CSR0_SVDSETUPEND;
REG_USB_CSR0 = csr0;
ep0_data_supplied = false;
ep0_data_requested = false;
- if (ep_send->busy)
- {
+ if (ep_send->busy) {
if (!ep_send->wait)
- usb_core_transfer_complete(0, USB_DIR_IN, -1, 0);
+ usb_core_transfer_complete(EP_CONTROL, USB_DIR_IN, -1, 0);
ep_transfer_completed(ep_send);
}
- if (ep_recv->busy)
- {
- usb_core_transfer_complete(0, USB_DIR_OUT, -1, 0);
+ if (ep_recv->busy) {
+ usb_core_transfer_complete(EP_CONTROL, USB_DIR_OUT, -1, 0);
ep_transfer_completed(ep_recv);
}
}
/* Call relevant routines for endpoint 0 state */
- if(csr0 & USB_CSR0_OUTPKTRDY) /* There is a packet in the fifo */
- {
- if (ep_send->busy)
- {
- if (!ep_send->wait)
- usb_core_transfer_complete(0, USB_DIR_IN, -1, 0);
+ if (csr0 & USB_CSR0_OUTPKTRDY) { /* There is a packet in the fifo */
+ if (ep_send->busy) {
+ if (!ep_send->wait) {
+ usb_core_transfer_complete(EP_CONTROL, USB_DIR_IN, -1, 0);
+ }
ep_transfer_completed(ep_send);
}
- if (ep_recv->busy && ep_recv->buf && ep_recv->length)
- {
+ if (ep_recv->busy && ep_recv->buf && ep_recv->length) {
unsigned int size = REG_USB_COUNT0;
readFIFO(ep_recv, size);
ep_recv->received += size;
- if (size < ep_recv->fifo_size || ep_recv->received >= ep_recv->length)
- {
+ if (size < ep_recv->fifo_size || ep_recv->received >= ep_recv->length) {
REG_USB_CSR0 = csr0 | USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND; /* Set data end! */
- usb_core_transfer_complete(0, USB_DIR_OUT, 0, ep_recv->received);
+ usb_core_transfer_complete(EP_CONTROL, USB_DIR_OUT, 0, ep_recv->received);
ep_transfer_completed(ep_recv);
+ } else {
+ REG_USB_CSR0 = csr0 | USB_CSR0_SVDOUTPKTRDY; /* clear OUTPKTRDY bit */
}
- else REG_USB_CSR0 = csr0 | USB_CSR0_SVDOUTPKTRDY; /* clear OUTPKTRDY bit */
- }
- else if (!ep0_data_supplied)
- {
+ } else if (!ep0_data_supplied) {
ep_recv->buf = ep0_rx.buf;
readFIFO(ep_recv, REG_USB_COUNT0);
csr0 |= USB_CSR0_SVDOUTPKTRDY;
- if (!ep0_rx.request.wLength)
- {
+ if (!ep0_rx.request.wLength) {
csr0 |= USB_CSR0_DATAEND; /* Set data end! */
ep0_data_requested = false;
ep0_data_supplied = false;
- }
- else if (ep0_rx.request.bRequestType & USB_DIR_IN)
+ } else if (ep0_rx.request.bRequestType & USB_DIR_IN) {
ep0_data_requested = true;
- else ep0_data_supplied = true;
+ } else {
+ ep0_data_supplied = true;
+ }
REG_USB_CSR0 = csr0;
- usb_core_control_request(&ep0_rx.request);
+ usb_core_legacy_control_request(&ep0_rx.request);
ep_transfer_completed(ep_recv);
}
}
- else if (ep_send->busy)
+ else if (ep_send->busy) {
EP0_send();
+ }
}
/* Does new work */
@@ -427,7 +406,7 @@ static void EPIN_send(unsigned int endpoint)
#endif
/* Non-DMA code */
- if(ep->sent == 0)
+ if (ep->sent == 0)
length = MIN(ep->length, ep->fifo_size);
else
length = MIN(EP_BUF_LEFT(ep), ep->fifo_size);
@@ -492,7 +471,7 @@ static void EPIN_complete(unsigned int endpoint)
logf("EP%d: %d -> %d", endpoint, ep->sent, ep->length);
- if(ep->sent >= ep->length) {
+ if (ep->sent >= ep->length) {
if (!ep->wait)
usb_core_transfer_complete(endpoint, USB_DIR_IN, 0, ep->sent);
ep->rc = 0;
@@ -514,9 +493,9 @@ static void EPOUT_handler(unsigned int endpoint)
}
select_endpoint(endpoint);
- while((csr = REG_USB_OUTCSR) & (USB_OUTCSR_SENTSTALL|USB_OUTCSR_OUTPKTRDY)) {
+ while ((csr = REG_USB_OUTCSR) & (USB_OUTCSR_SENTSTALL|USB_OUTCSR_OUTPKTRDY)) {
logf("%s(%d): 0x%x", __func__, endpoint, csr);
- if(csr & USB_OUTCSR_SENTSTALL) {
+ if (csr & USB_OUTCSR_SENTSTALL) {
logf("stall sent, flushing fifo..");
flushFIFO(ep);
REG_USB_OUTCSR = csr & ~USB_OUTCSR_SENTSTALL;
@@ -591,7 +570,7 @@ static void EPOUT_handler(unsigned int endpoint)
logf("received: %d max length: %d", ep->received, ep->length);
- if(size < ep->fifo_size || ep->received >= ep->length) {
+ if (size < ep->fifo_size || ep->received >= ep->length) {
usb_core_transfer_complete(endpoint, USB_DIR_OUT, 0, ep->received);
ep_transfer_completed(ep);
logf("receive transfer_complete");
@@ -611,8 +590,7 @@ static void EPOUT_ready(unsigned int endpoint)
select_endpoint(endpoint);
csr = REG_USB_OUTCSR;
- if(!ep->busy)
- {
+ if (!ep->busy) {
logf("Entered EPOUT handler without work!");
return;
}
@@ -715,15 +693,15 @@ static void setup_endpoint(struct usb_endpoint *ep)
select_endpoint(endpoint);
- if (ep->busy)
- {
- if(EP_IS_IN(ep))
- {
+ if (ep->busy) {
+ if (EP_IS_IN(ep)) {
if (ep->wait)
semaphore_release(&ep->complete);
- else usb_core_transfer_complete(endpoint, USB_DIR_IN, -1, 0);
+ else
+ usb_core_transfer_complete(endpoint, USB_DIR_IN, -1, 0);
+ } else {
+ usb_core_transfer_complete(endpoint, USB_DIR_OUT, -1, 0);
}
- else usb_core_transfer_complete(endpoint, USB_DIR_OUT, -1, 0);
}
ep->busy = false;
@@ -731,17 +709,16 @@ static void setup_endpoint(struct usb_endpoint *ep)
ep->sent = 0;
ep->length = 0;
- if(ep->type != ep_control)
+ if (ep->type != ep_control)
ep->fifo_size = usb_drv_port_speed() ? 512 : 64;
ep->config = REG_USB_CONFIGDATA;
- if(EP_IS_IN(ep))
- {
+ if(EP_IS_IN(ep)) {
csr = (USB_INCSR_FF | USB_INCSR_CDT);
csrh = USB_INCSRH_MODE;
- if(ep->type == ep_interrupt)
+ if (ep->type == ep_interrupt)
csrh |= USB_INCSRH_FRCDATATOG;
REG_USB_INMAXP = ep->fifo_size;
@@ -752,9 +729,7 @@ static void setup_endpoint(struct usb_endpoint *ep)
if (ep->allocated)
REG_USB_INTRINE |= USB_INTR_EP(EP_NUMBER2(ep));
- }
- else
- {
+ } else {
csr = (USB_OUTCSR_FF | USB_OUTCSR_CDT);
csrh = 0;
@@ -811,11 +786,11 @@ static void udc_reset(void)
endpoints[0].config = REG_USB_CONFIGDATA;
endpoints[1].config = REG_USB_CONFIGDATA;
- if (endpoints[0].busy)
- {
+ if (endpoints[0].busy) {
if (endpoints[0].wait)
semaphore_release(&endpoints[0].complete);
- else usb_core_transfer_complete(0, USB_DIR_IN, -1, 0);
+ else
+ usb_core_transfer_complete(EP_CONTROL, USB_DIR_IN, -1, 0);
}
endpoints[0].busy = false;
@@ -825,7 +800,7 @@ static void udc_reset(void)
endpoints[0].allocated = true;
if (endpoints[1].busy)
- usb_core_transfer_complete(0, USB_DIR_OUT, -1, 0);
+ usb_core_transfer_complete(EP_CONTROL, USB_DIR_OUT, -1, 0);
endpoints[1].busy = false;
endpoints[1].wait = false;
@@ -834,7 +809,7 @@ static void udc_reset(void)
endpoints[1].allocated = true;
/* Reset other endpoints */
- for(i=2; i<TOTAL_EP(); i++)
+ for (i=2; i<TOTAL_EP(); i++)
setup_endpoint(&endpoints[i]);
ep0_data_supplied = false;
@@ -861,26 +836,26 @@ void OTG(void)
logf("IRQ %x %x %x %x", intrUSB, intrIn, intrOut, intrDMA);
/* EPIN & EPOUT are all handled in DMA */
- if(intrIn & USB_INTR_EP(0))
+ if (intrIn & USB_INTR_EP(0))
EP0_handler();
- if(intrIn & USB_INTR_EP(1))
+ if (intrIn & USB_INTR_EP(1))
EPIN_complete(1);
- if(intrIn & USB_INTR_EP(2))
+ if (intrIn & USB_INTR_EP(2))
EPIN_complete(2);
- if(intrOut & USB_INTR_EP(1))
+ if (intrOut & USB_INTR_EP(1))
EPOUT_ready(1);
- if(intrOut & USB_INTR_EP(2))
+ if (intrOut & USB_INTR_EP(2))
EPOUT_ready(2);
- if(intrUSB & USB_INTR_RESET)
+ if (intrUSB & USB_INTR_RESET)
udc_reset();
- if(intrUSB & USB_INTR_SUSPEND)
+ if (intrUSB & USB_INTR_SUSPEND)
logf("USB suspend");
- if(intrUSB & USB_INTR_RESUME)
+ if (intrUSB & USB_INTR_RESUME)
logf("USB resume");
#ifdef USE_USB_DMA
- if(intrDMA & (1<<USB_INTR_DMA_BULKIN))
+ if (intrDMA & (1<<USB_INTR_DMA_BULKIN))
EPDMA_handler(USB_INTR_DMA_BULKIN);
- if(intrDMA & (1<<USB_INTR_DMA_BULKOUT))
+ if (intrDMA & (1<<USB_INTR_DMA_BULKOUT))
EPDMA_handler(USB_INTR_DMA_BULKOUT);
#endif
}
@@ -893,11 +868,10 @@ bool usb_drv_stalled(int endpoint, bool in)
select_endpoint(endpoint);
- if(endpoint == EP_CONTROL)
+ if (endpoint == EP_CONTROL) {
return (REG_USB_CSR0 & USB_CSR0_SENDSTALL) != 0;
- else
- {
- if(in)
+ } else {
+ if (in)
return (REG_USB_INCSR & USB_INCSR_SENDSTALL) != 0;
else
return (REG_USB_OUTCSR & USB_OUTCSR_SENDSTALL) != 0;
@@ -912,24 +886,18 @@ void usb_drv_stall(int endpoint, bool stall, bool in)
select_endpoint(endpoint);
- if(endpoint == EP_CONTROL)
- {
+ if(endpoint == EP_CONTROL) {
if(stall)
REG_USB_CSR0 |= USB_CSR0_SENDSTALL;
else
REG_USB_CSR0 &= ~USB_CSR0_SENDSTALL;
- }
- else
- {
- if(in)
- {
+ } else {
+ if(in) {
if(stall)
REG_USB_INCSR |= USB_INCSR_SENDSTALL;
else
REG_USB_INCSR = (REG_USB_INCSR & ~USB_INCSR_SENDSTALL) | USB_INCSR_CDT;
- }
- else
- {
+ } else {
if(stall)
REG_USB_OUTCSR |= USB_OUTCSR_SENDSTALL;
else
@@ -962,7 +930,7 @@ void usb_init_device(void)
system_enable_irq(IRQ_OTG);
- for(unsigned i=0; i<TOTAL_EP(); i++)
+ for (unsigned i=0; i<TOTAL_EP(); i++)
semaphore_init(&endpoints[i].complete, 1, 0);
}
@@ -976,7 +944,7 @@ static int usb_oneshot_callback(struct timeout *tmo)
* and post appropriate event. */
usb_status_event(state);
- if(state == USB_EXTRACTED)
+ if (state == USB_EXTRACTED)
__gpio_as_irq_rise_edge(PIN_USB_DET);
else
__gpio_as_irq_fall_edge(PIN_USB_DET);
@@ -993,7 +961,7 @@ void GPIO_USB_DET(void)
void usb_enable(bool on)
{
- if(on)
+ if (on)
usb_core_init();
else
usb_core_exit();
@@ -1067,7 +1035,7 @@ static void usb_drv_send_internal(struct usb_endpoint* ep, void* ptr, int length
ep->sent = 0;
ep->length = length;
ep->busy = true;
- if(blocking) {
+ if (blocking) {
ep->rc = -1;
ep->wait = true;
} else {
@@ -1082,7 +1050,7 @@ static void usb_drv_send_internal(struct usb_endpoint* ep, void* ptr, int length
restore_irq(flags);
- if(blocking) {
+ if (blocking) {
semaphore_wait(&ep->complete, HZ);
ep->wait = false;
}
@@ -1094,8 +1062,7 @@ int usb_drv_send_nonblocking(int endpoint, void* ptr, int length)
logf("%s(%d, 0x%x, %d)", __func__, endpoint, (int)ptr, length);
- if (ep->allocated)
- {
+ if (ep->allocated) {
usb_drv_send_internal(ep, ptr, length, false);
return 0;
}
@@ -1109,8 +1076,7 @@ int usb_drv_send(int endpoint, void* ptr, int length)
logf("%s(%d, 0x%x, %d)", __func__, endpoint, (int)ptr, length);
- if (ep->allocated)
- {
+ if (ep->allocated) {
usb_drv_send_internal(ep, ptr, length, true);
return ep->rc;
}
@@ -1118,7 +1084,7 @@ int usb_drv_send(int endpoint, void* ptr, int length)
return -1;
}
-int usb_drv_recv(int endpoint, void* ptr, int length)
+int usb_drv_recv_nonblocking(int endpoint, void* ptr, int length)
{
int flags;
struct usb_endpoint *ep;
@@ -1185,7 +1151,8 @@ void usb_drv_cancel_all_transfers(void)
{
logf("%s()", __func__);
- unsigned int i, flags = disable_irq_save();
+ unsigned int i;
+ unsigned int flags = disable_irq_save();
#ifdef USE_USB_DMA
/* Disable DMA */
@@ -1201,7 +1168,8 @@ void usb_drv_cancel_all_transfers(void)
usb_core_transfer_complete(i >> 1, USB_DIR_OUT, -1, 0);
else if (endpoints[i].wait)
semaphore_release(&endpoints[i].complete);
- else usb_core_transfer_complete(i >> 1, USB_DIR_IN, -1, 0);
+ else
+ usb_core_transfer_complete(i >> 1, USB_DIR_IN, -1, 0);
}
if(i != 1) /* ep0 out needs special handling */
diff --git a/firmware/target/mips/ingenic_jz47xx/xduoo_x3/lcd-xduoo_x3.c b/firmware/target/mips/ingenic_jz47xx/xduoo_x3/lcd-xduoo_x3.c
index 47b646be34..2c1c3a226e 100644
--- a/firmware/target/mips/ingenic_jz47xx/xduoo_x3/lcd-xduoo_x3.c
+++ b/firmware/target/mips/ingenic_jz47xx/xduoo_x3/lcd-xduoo_x3.c
@@ -532,6 +532,7 @@ void lcd_update(void)
const int column_high = get_column_high_byte(0);
const int column_low = get_column_low_byte(0);
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
/* Copy display bitmap to hardware */
for (y = 0; y < LCD_FBHEIGHT; y++)
{
@@ -542,7 +543,7 @@ void lcd_update(void)
(column_low)
);
- lcd_write_data (FBADDR(0, y), LCD_WIDTH);
+ lcd_write_data (fbaddr(0,y), LCD_WIDTH);
}
}
@@ -586,6 +587,7 @@ void lcd_update_rect(int x, int y, int width, int height)
ymax = (y + height-1) >> 3;
y >>= 3;
+ void* (*fbaddr)(int x, int y) = FB_CURRENTVP_BUFFER->get_address_fn;
/* Copy specified rectange bitmap to hardware */
for (; y <= ymax; y++)
{
@@ -596,6 +598,6 @@ void lcd_update_rect(int x, int y, int width, int height)
(column_low)
);
- lcd_write_data (FBADDR(x,y), width);
+ lcd_write_data (fbaddr(x,y), width);
}
}
diff --git a/firmware/target/mips/ingenic_x1000/aic-x1000.c b/firmware/target/mips/ingenic_x1000/aic-x1000.c
index a0e509d3b6..ff9802501d 100644
--- a/firmware/target/mips/ingenic_x1000/aic-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/aic-x1000.c
@@ -31,12 +31,12 @@
* is complete if this value is less than "cnt", and may be incomplete if it
* is equal to "cnt". (Note the leading zero term is not written to "buf".)
*/
-static unsigned cf_derive(unsigned m, unsigned n, unsigned* buf, unsigned cnt)
+static uint32_t cf_derive(uint32_t m, uint32_t n, uint32_t* buf, uint32_t cnt)
{
- unsigned wrote = 0;
- unsigned a = m / n;
+ uint32_t wrote = 0;
+ uint32_t a = m / n;
while(cnt--) {
- unsigned tmp = n;
+ uint32_t tmp = n;
n = m - n * a;
if(n == 0)
break;
@@ -54,16 +54,16 @@ static unsigned cf_derive(unsigned m, unsigned n, unsigned* buf, unsigned cnt)
* calculate the rational number m/n which it represents. Returns m and n.
* If count is zero, then m and n are undefined.
*/
-static void cf_expand(const unsigned* buf, unsigned count,
- unsigned* m, unsigned* n)
+static void cf_expand(const uint32_t* buf, uint32_t count,
+ uint32_t* m, uint32_t* n)
{
if(count == 0)
return;
- unsigned i = count - 1;
- unsigned mx = 1, nx = buf[i];
+ uint32_t i = count - 1;
+ uint32_t mx = 1, nx = buf[i];
while(i--) {
- unsigned tmp = nx;
+ uint32_t tmp = nx;
nx = mx + buf[i] * nx;
mx = tmp;
}
@@ -72,48 +72,104 @@ static void cf_expand(const unsigned* buf, unsigned count,
*n = nx;
}
-int aic_i2s_set_mclk(x1000_clk_t clksrc, unsigned fs, unsigned mult)
+static int calc_i2s_clock_params(x1000_clk_t clksrc,
+ uint32_t fs, uint32_t mult,
+ uint32_t* div_m, uint32_t* div_n,
+ uint32_t* i2sdiv)
{
- /* get the input clock rate */
- uint32_t src_freq = clk_get(clksrc);
+ if(clksrc == X1000_CLK_EXCLK) {
+ /* EXCLK mode bypasses the CPM clock so it's more limited */
+ *div_m = 0;
+ *div_n = 0;
+ *i2sdiv = X1000_EXCLK_FREQ / 64 / fs;
+
+ /* clamp to maximum value */
+ if(*i2sdiv > 512)
+ *i2sdiv = 512;
+ if(*i2sdiv == 0)
+ *i2sdiv = 1;
+
+ return 0;
+ }
- /* reject invalid parameters */
+ /* ensure a valid clock was selected */
+ if(clksrc != X1000_CLK_SCLK_A &&
+ clksrc != X1000_CLK_MPLL)
+ return -1;
+
+ /* ensure bit clock constraint is respected */
if(mult % 64 != 0)
return -1;
- if(clksrc == X1000_EXCLK_FREQ) {
- if(mult != 0)
- return -1;
+ /* ensure master clock frequency is not too high */
+ if(fs > UINT32_MAX/mult)
+ return -1;
+
+ /* get frequencies */
+ uint32_t tgt_freq = fs * mult;
+ uint32_t src_freq = clk_get(clksrc);
+
+ /* calculate best rational approximation fitting hardware constraints */
+ uint32_t m = 0, n = 0;
+ uint32_t buf[16];
+ uint32_t cnt = cf_derive(tgt_freq, src_freq, &buf[0], 16);
+ do {
+ cf_expand(&buf[0], cnt, &m, &n);
+ cnt -= 1;
+ } while(cnt > 0 && (m > 512 || n > 8192) && (n >= 2*m));
- jz_writef(AIC_I2SCR, STPBK(1));
+ /* unrepresentable */
+ if(cnt == 0 || n == 0 || m == 0)
+ return -1;
+
+ *div_m = m;
+ *div_n = n;
+ *i2sdiv = mult / 64;
+ return 0;
+}
+
+uint32_t aic_calc_i2s_clock(x1000_clk_t clksrc, uint32_t fs, uint32_t mult)
+{
+ uint32_t m, n, i2sdiv;
+ if(calc_i2s_clock_params(clksrc, fs, mult, &m, &n, &i2sdiv))
+ return 0;
+
+ unsigned long long rate = clk_get(clksrc);
+ rate *= m;
+ rate /= n * i2sdiv; /* this multiply can't overflow. */
+
+ /* clamp */
+ if(rate > 0xffffffffull)
+ rate = 0xffffffff;
+
+ return rate;
+}
+
+int aic_set_i2s_clock(x1000_clk_t clksrc, uint32_t fs, uint32_t mult)
+{
+ uint32_t m, n, i2sdiv;
+ if(calc_i2s_clock_params(clksrc, fs, mult, &m, &n, &i2sdiv))
+ return -1;
+
+ /* turn off bit clock */
+ bool bitclock_en = !jz_readf(AIC_I2SCR, STPBK);
+ jz_writef(AIC_I2SCR, STPBK(1));
+
+ /* handle master clock */
+ if(clksrc == X1000_CLK_EXCLK) {
jz_writef(CPM_I2SCDR, CS(0), CE(0));
- REG_AIC_I2SDIV = X1000_EXCLK_FREQ / 64 / fs;
} else {
- if(mult == 0)
- return -1;
- if(fs*mult > src_freq)
- return -1;
-
- /* calculate best rational approximation that fits our constraints */
- unsigned m = 0, n = 0;
- unsigned buf[16];
- unsigned cnt = cf_derive(fs*mult, src_freq, &buf[0], 16);
- do {
- cf_expand(&buf[0], cnt, &m, &n);
- cnt -= 1;
- } while(cnt > 0 && (m > 512 || n > 8192) && (n >= 2*m));
-
- /* wrong values */
- if(cnt == 0 || n == 0 || m == 0)
- return -1;
-
- jz_writef(AIC_I2SCR, STPBK(1));
jz_writef(CPM_I2SCDR, PCS(clksrc == X1000_CLK_MPLL ? 1 : 0),
CS(1), CE(1), DIV_M(m), DIV_N(n));
jz_write(CPM_I2SCDR1, REG_CPM_I2SCDR1);
- REG_AIC_I2SDIV = (mult / 64) - 1;
}
- jz_writef(AIC_I2SCR, STPBK(0));
+ /* set bit clock divider */
+ REG_AIC_I2SDIV = i2sdiv - 1;
+
+ /* re-enable the bit clock */
+ if(bitclock_en)
+ jz_writef(AIC_I2SCR, STPBK(0));
+
return 0;
}
diff --git a/firmware/target/mips/ingenic_x1000/aic-x1000.h b/firmware/target/mips/ingenic_x1000/aic-x1000.h
index eda0f80f04..f272655b9c 100644
--- a/firmware/target/mips/ingenic_x1000/aic-x1000.h
+++ b/firmware/target/mips/ingenic_x1000/aic-x1000.h
@@ -23,24 +23,122 @@
#define __AIC_X1000_H__
#include "clk-x1000.h"
+#include "x1000/aic.h"
#include <stdbool.h>
+#include <stdint.h>
-/* Set frequency of I2S master clock supplied by AIC. Has no use if an
- * external DAC is supplying the master clock. Must be called with the
- * bit clock disabled.
- *
- * - clksrc can be one of EXCLK, SCLK_A, MPLL.
- * - This function does not modify PLL settings. It's the caller's job
- * to ensure the PLL is configured and runing.
- * - fs is the audio sampling frequency (8 KHz - 192 KHz)
- * - mult is multiplied by fs to get the master clock rate.
- * - mult must be a multiple of 64 due to AIC bit clock requirements.
- * - Note: EXCLK bypasses the decimal divider so it is not very flexible.
- * If using EXCLK you must set mult=0. If EXCLK is not a multiple of
- * the bit clock (= 64*fs), then the clock rate will be inaccurate.
- *
- * Returns zero on success and nonzero if the frequency is not achievable.
+#define AIC_I2S_MASTER_MODE 0
+#define AIC_I2S_MASTER_EXCLK_MODE 1
+#define AIC_I2S_SLAVE_MODE 2
+
+#define AIC_I2S_LEFT_CHANNEL_FIRST 0
+#define AIC_I2S_RIGHT_CHANNEL_FIRST 1
+
+/* Nb. the functions below are intended to serve as "documentation" and make
+ * target audiohw code clearer, they should normally be called with immediate
+ * constant arguments so they are inlined to a register read-modify-write. */
+
+/* Enable/disable some kind of big-endian mode. Presumably it refers to
+ * the endianness of the samples read or written to the FIFO. */
+static inline void aic_set_big_endian_format(bool en)
+{
+ jz_writef(AIC_CFG, MSB(en ? 1 : 0));
+}
+
+/* Set whether to send the last sample (true) or a zero sample (false)
+ * if the AIC FIFO underflows during playback. */
+static inline void aic_set_play_last_sample(bool en)
+{
+ jz_writef(AIC_CFG, LSMP(en ? 1 : 0));
+}
+
+/* Select the use of the internal or external codec. */
+static inline void aic_set_external_codec(bool en)
+{
+ jz_writef(AIC_CFG, ICDC(en ? 0 : 1));
+}
+
+/* Set I2S interface mode */
+static inline void aic_set_i2s_mode(int mode)
+{
+ switch(mode) {
+ default:
+ case AIC_I2S_MASTER_MODE:
+ jz_writef(AIC_CFG, BCKD(1), SYNCD(1));
+ break;
+
+ case AIC_I2S_MASTER_EXCLK_MODE:
+ jz_writef(AIC_CFG, BCKD(0), SYNCD(1));
+ break;
+
+ case AIC_I2S_SLAVE_MODE:
+ jz_writef(AIC_CFG, BCKD(0), SYNCD(0));
+ break;
+ }
+}
+
+/* Select the channel ordering on the I2S interface (playback only). */
+static inline void aic_set_i2s_channel_order(int order)
+{
+ switch(order) {
+ default:
+ case AIC_I2S_LEFT_CHANNEL_FIRST:
+ jz_writef(AIC_I2SCR, RFIRST(0));
+ break;
+
+ case AIC_I2S_RIGHT_CHANNEL_FIRST:
+ jz_writef(AIC_I2SCR, RFIRST(1));
+ break;
+ }
+}
+
+/* Enable/disable the I2S master clock (also called 'system clock') */
+static inline void aic_enable_i2s_master_clock(bool en)
+{
+ jz_writef(AIC_I2SCR, ESCLK(en ? 1 : 0));
+}
+
+/* Enable/disable the I2S bit clock */
+static inline void aic_enable_i2s_bit_clock(bool en)
+{
+ jz_writef(AIC_I2SCR, STPBK(en ? 0 : 1));
+}
+
+/* Select whether I2S mode is used (false) or MSB-justified mode (true). */
+static inline void aic_set_msb_justified_mode(bool en)
+{
+ jz_writef(AIC_I2SCR, AMSL(en ? 1 : 0));
+}
+
+/* Calculate frequency of I2S clocks.
+ *
+ * - 'clksrc' can be one of EXCLK, SCLK_A, or MPLL.
+ * - 'fs' is the audio sampling frequency in Hz, must be 8 KHz - 192 KHz.
+ * - The master clock frequency equals 'mult * fs' Hz. Due to hardware
+ * restrictions, 'mult' must be divisible by 64.
+ *
+ * - NOTE: When using EXCLK source, the master clock equals EXCLK and the
+ * 'mult' parameter is ignored.
+ *
+ * This function returns the actual bit clock rate which would be achieved.
+ * (Note the bit clock is always 64x the effective sampling rate.)
+ *
+ * If the exact rate cannot be attained, then this will return the closest
+ * possible rate to the desired rate. In case of invalid parameters, this
+ * function will return zero. That also occurs if the chosen PLL is stopped.
+ */
+extern uint32_t aic_calc_i2s_clock(x1000_clk_t clksrc,
+ uint32_t fs, uint32_t mult);
+
+/* Set the I2S clock frequency.
+ *
+ * Parameters are the same as 'aic_calc_i2s_clock()' except this function
+ * will set the clocks. If the bit clock is running, it will be automatically
+ * stopped and restarted properly.
+ *
+ * Returns zero on success. If an invalid state occurs (due to bad settings)
+ * then this function will do nothing and return a nonzero value.
*/
-extern int aic_i2s_set_mclk(x1000_clk_t clksrc, unsigned fs, unsigned mult);
+extern int aic_set_i2s_clock(x1000_clk_t clksrc, uint32_t fs, uint32_t mult);
#endif /* __AIC_X1000_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/app.lds b/firmware/target/mips/ingenic_x1000/app.lds
index 0f6352b8ee..fe06e1cd8d 100644
--- a/firmware/target/mips/ingenic_x1000/app.lds
+++ b/firmware/target/mips/ingenic_x1000/app.lds
@@ -1,40 +1,57 @@
#include "config.h"
+#include "cpu.h"
OUTPUT_FORMAT("elf32-littlemips")
OUTPUT_ARCH(MIPS)
ENTRY(_start)
STARTUP(target/mips/ingenic_x1000/crt0.o)
+INPUT(target/mips/exception-mips.o)
+INPUT(target/mips/system-mips.o)
-/* Stub area is used for loading new firmware via RoLo */
-#define STUBSIZE 0x4000
-#define SDRAM_ORIG 0x80000000
-
-/* IRAM contains stub, DRAM contains main app */
-#define IRAMORIG SDRAM_ORIG
-#define IRAMSIZE STUBSIZE
-#define DRAMORIG (SDRAM_ORIG + STUBSIZE)
-#define DRAMSIZE (MEMORYSIZE * 0x100000 - STUBSIZE)
+#ifdef BOOTLOADER
+# undef PLUGIN_BUFFER_SIZE
+# undef CODEC_SIZE
+# define PLUGIN_BUFFER_SIZE 0
+# define CODEC_SIZE 0
+# if defined(HAVE_INIT_ATTR)
+/* This needs to be fixed for the bootloader */
+# error "bootloader does not support INIT_ATTR"
+# endif
+#endif
/* End of the audio buffer, where the codec buffer starts */
-#define ENDAUDIOADDR (DRAMORIG + DRAMSIZE - PLUGIN_BUFFER_SIZE - CODEC_SIZE)
+#define ENDAUDIOADDR (X1000_DRAM_END - PLUGIN_BUFFER_SIZE - CODEC_SIZE)
/* Where the codec buffer ends, and the plugin buffer starts */
#define ENDCODECADDR (ENDAUDIOADDR + CODEC_SIZE)
+/* Place init code in the codec buffer */
+#define INIT_BASE ENDAUDIOADDR
+#define INIT_SIZE CODEC_SIZE
+
MEMORY
{
- IRAM : ORIGIN = IRAMORIG, LENGTH = IRAMSIZE
- DRAM : ORIGIN = DRAMORIG, LENGTH = DRAMSIZE
+ IRAM : ORIGIN = X1000_IRAM_BASE, LENGTH = X1000_IRAM_SIZE
+ DRAM : ORIGIN = X1000_DRAM_BASE, LENGTH = X1000_DRAM_SIZE
+ TCSM : ORIGIN = X1000_TCSM_BASE, LENGTH = X1000_TCSM_SIZE
+ INIT : ORIGIN = INIT_BASE, LENGTH = INIT_SIZE
}
SECTIONS
{
- .text :
+ .startup :
{
loadaddress = .;
_loadaddress = .;
- *(.init.text);
+ *(.startup.text);
+ } > DRAM
+
+ .text :
+ {
*(.text*);
+#ifndef HAVE_INIT_ATTR
+ *(.init*);
+#endif
} > DRAM
. = ALIGN(4);
@@ -50,7 +67,13 @@ SECTIONS
*(.sdata*);
} > DRAM
- .iram IRAMORIG: AT (_bssbegin)
+ /*
+ * The following sections are loaded after normal DRAM sections
+ * but are copied elsewhere by the startup code.
+ */
+ _noloaddram = .;
+
+ .iram :
{
_iramstart = .;
. = 0x000; /* TLB refill */
@@ -63,26 +86,46 @@ SECTIONS
KEEP(*(.vectors.4));
KEEP(*(.vectors));
- *(.icode);
+ *(.icode*);
*(.irodata);
*(.idata);
_iramend = .;
- } > IRAM
+ } > IRAM AT> DRAM
_iramcopy = LOADADDR(.iram);
+ .tcsm :
+ {
+ _tcsmstart = .;
+ KEEP(*(.tcsm*));
+ _tcsmend = .;
+ } > TCSM AT> DRAM
+ _tcsmcopy = LOADADDR(.tcsm);
+
+#ifdef HAVE_INIT_ATTR
+ .init :
+ {
+ _initstart = .;
+ *(.init*);
+ _initend = .;
+ } > INIT AT> DRAM
+ _initcopy = LOADADDR(.init);
+#endif
+
+ /* Sections below have no data. */
+
. = ALIGN(4);
.stack (NOLOAD) :
{
*(.stack);
stackbegin = .;
- . += 0x1E00;
+ . += X1000_STACKSIZE;
stackend = .;
_irqstackbegin = .;
- . += 0x300;
+ . += X1000_IRQSTACKSIZE;
_irqstackend = .;
} > IRAM
- .bss (NOLOAD) :
+ .bss _noloaddram (NOLOAD) :
{
_bssbegin = .;
*(.sbss*);
@@ -93,26 +136,19 @@ SECTIONS
_end = .;
} > DRAM
-#ifdef BOOTLOADER
- . = ALIGN(4);
- loadbuffer = .;
- . += 0x100000 * 4; /* Allow 4 MiB for the rockbox binary */
- loadbufferend = .;
-#else
-
.audiobuf :
{
- . = ALIGN(4); /* XXX might need more alignment here */
+ . = ALIGN(4);
audiobuffer = .;
} > DRAM
audiobufend = ENDAUDIOADDR;
codecbuf = ENDAUDIOADDR;
pluginbuf = ENDCODECADDR;
-#endif
/DISCARD/ :
{
+ *(.MIPS.abiflags);
*(.eh_frame);
*(.rel.dyn);
}
diff --git a/firmware/target/mips/ingenic_x1000/boot-x1000.c b/firmware/target/mips/ingenic_x1000/boot-x1000.c
new file mode 100644
index 0000000000..2f2714c67a
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/boot-x1000.c
@@ -0,0 +1,285 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2022 Aidan MacDonald
+ *
+ * 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 "usb.h"
+#include "boot-x1000.h"
+#include "nand-x1000.h"
+#include "gpio-x1000.h"
+#include "clk-x1000.h"
+#include "x1000/cpm.h"
+#include "x1000/lcd.h"
+#include "x1000/uart.h"
+#include <string.h>
+
+#define HDR_BEGIN 128 /* header must begin within this many bytes */
+#define HDR_LEN 256 /* header length cannot exceed this */
+
+/* search for header value, label must be a 4-character string.
+ * Returns the found value or 0 if the label wasn't found. */
+static uint32_t search_header(const unsigned char* source, size_t length,
+ const char* label)
+{
+ size_t search_len = MIN(length, HDR_BEGIN);
+ if(search_len < 8)
+ return 0;
+ search_len -= 7;
+
+ /* find the beginning marker */
+ size_t i;
+ for(i = 8; i < search_len; i += 4)
+ if(!memcmp(&source[i], "BEGINHDR", 8))
+ break;
+ if(i >= search_len)
+ return 0;
+ i += 8;
+
+ /* search within the header */
+ search_len = MIN(length, i + HDR_LEN) - 7;
+ for(; i < search_len; i += 8) {
+ if(!memcmp(&source[i], "ENDH", 4)) {
+ break;
+ } else if(!memcmp(&source[i], label, 4)) {
+ i += 4;
+ /* read little-endian value */
+ uint32_t ret = source[i];
+ ret |= source[i+1] << 8;
+ ret |= source[i+2] << 16;
+ ret |= source[i+3] << 24;
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void iram_memmove(void* dest, const void* source, size_t length)
+ __attribute__((section(".icode")));
+
+static void iram_memmove(void* dest, const void* source, size_t length)
+{
+ unsigned char* d = dest;
+ const unsigned char* s = source;
+
+ if(s < d && d < s + length) {
+ d += length;
+ s += length;
+ while(length--)
+ *--d = *--s;
+ } else {
+ while(length--)
+ *d++ = *s++;
+ }
+}
+
+void x1000_boot_rockbox(const void* source, size_t length)
+{
+ uint32_t load_addr = search_header(source, length, "LOAD");
+ if(!load_addr)
+ load_addr = X1000_STANDARD_DRAM_BASE;
+
+ disable_irq();
+
+ /* --- Beyond this point, do not call into DRAM --- */
+
+ iram_memmove((void*)load_addr, source, length);
+ commit_discard_idcache();
+
+ typedef void(*entry_fn)(void);
+ entry_fn fn = (entry_fn)load_addr;
+ fn();
+ while(1);
+}
+
+void x1000_boot_linux(const void* source, size_t length,
+ void* load, void* entry, const char* args)
+{
+ size_t args_len = strlen(args);
+
+ /* Shut off USB to avoid "irq 21 nobody cared" error */
+ usb_close();
+ usb_enable(false);
+
+ /* clear USB PHY voodoo bits, not all kernels use them */
+ jz_writef(CPM_OPCR, GATE_USBPHY_CLK(0));
+ jz_writef(CPM_USBCDR, PHY_GATE(0));
+
+ disable_irq();
+
+ /* --- Beyond this point, do not call into DRAM --- */
+
+ void* safe_mem = (void*)X1000_IRAM_END;
+
+ /* copy argument string to a safe location */
+ char* args_copy = safe_mem + 32;
+ iram_memmove(args_copy, args, args_len+1);
+
+ /* generate argv array */
+ char** argv = safe_mem;
+ argv[0] = NULL;
+ argv[1] = args_copy;
+
+ iram_memmove(load, source, length);
+ commit_discard_idcache();
+
+ typedef void(*entry_fn)(long, char**, long, long);
+ entry_fn fn = (entry_fn)entry;
+ fn(2, argv, 0, 0);
+ while(1);
+}
+
+void rolo_restart(const unsigned char* source, unsigned char* dest, int length)
+{
+ (void)dest;
+ x1000_boot_rockbox(source, length);
+}
+
+void x1000_dualboot_cleanup(void)
+{
+ /* - disable all LCD interrupts since the M3K can't cope with them
+ * - disable BEDN bit since it creates garbled graphics on the Q1 */
+ jz_writef(CPM_CLKGR, LCD(0));
+ jz_writef(LCD_CTRL, BEDN(0), EOFM(0), SOFM(0), IFUM(0), QDM(0));
+ jz_writef(CPM_CLKGR, LCD(1));
+
+#if defined(FIIO_M3K) || defined(EROS_QN)
+ /*
+ * Need to bring up MPLL before booting Linux
+ * (Doesn't apply to Q1 since it sticks with the default Ingenic config)
+ */
+
+ /* 24 MHz * 25 = 600 MHz */
+ jz_writef(CPM_MPCR, BS(1), PLLM(25 - 1), PLLN(0), PLLOD(0), ENABLE(1));
+ while(jz_readf(CPM_MPCR, ON) == 0);
+
+ /* 600 MHz / 3 = 200 MHz */
+ clk_set_ddr(X1000_CLK_MPLL, 3);
+
+ clk_set_ccr_mux(CLKMUX_SCLK_A(APLL) |
+ CLKMUX_CPU(SCLK_A) |
+ CLKMUX_AHB0(MPLL) |
+ CLKMUX_AHB2(MPLL));
+ clk_set_ccr_div(CLKDIV_CPU(1) | /* 1008 MHz */
+ CLKDIV_L2(2) | /* 504 MHz */
+ CLKDIV_AHB0(3) | /* 200 MHz */
+ CLKDIV_AHB2(3) | /* 200 MHz */
+ CLKDIV_PCLK(6)); /* 100 MHz */
+#endif
+}
+
+void x1000_dualboot_init_clocktree(void)
+{
+ /* Make sure these are gated to match the OF behavior. */
+ jz_writef(CPM_CLKGR, PCM(1), MAC(1), LCD(1), MSC0(1), MSC1(1), OTG(1), CIM(1));
+
+ /* Set clock sources, and make sure every clock starts out stopped */
+ jz_writef(CPM_I2SCDR, CS_V(EXCLK));
+ jz_writef(CPM_PCMCDR, CS_V(EXCLK));
+
+ jz_writef(CPM_MACCDR, CLKSRC_V(MPLL), CE(1), STOP(1), CLKDIV(0xfe));
+ while(jz_readf(CPM_MACCDR, BUSY));
+
+ jz_writef(CPM_LPCDR, CLKSRC_V(MPLL), CE(1), STOP(1), CLKDIV(0xfe));
+ while(jz_readf(CPM_LPCDR, BUSY));
+
+ jz_writef(CPM_MSC0CDR, CLKSRC_V(MPLL), CE(1), STOP(1), CLKDIV(0xfe));
+ while(jz_readf(CPM_MSC0CDR, BUSY));
+
+ jz_writef(CPM_MSC1CDR, CE(1), STOP(1), CLKDIV(0xfe));
+ while(jz_readf(CPM_MSC1CDR, BUSY));
+
+ jz_writef(CPM_CIMCDR, CLKSRC_V(MPLL), CE(1), STOP(1), CLKDIV(0xfe));
+ while(jz_readf(CPM_CIMCDR, BUSY));
+
+ jz_writef(CPM_USBCDR, CLKSRC_V(EXCLK), CE(1), STOP(1));
+ while(jz_readf(CPM_USBCDR, BUSY));
+}
+
+void x1000_dualboot_init_uart2(void)
+{
+ /* Ungate the clock and select UART2 device function */
+ jz_writef(CPM_CLKGR, UART2(0));
+ gpioz_configure(GPIO_C, 3 << 30, GPIOF_DEVICE(1));
+
+ /* Disable all interrupts */
+ jz_write(UART_UIER(2), 0);
+
+ /* FIFO configuration */
+ jz_overwritef(UART_UFCR(2),
+ RDTR(3), /* FIFO trigger level = 60? */
+ UME(0), /* UART module disable */
+ DME(1), /* DMA mode enable? */
+ TFRT(1), /* transmit FIFO reset */
+ RFRT(1), /* receive FIFO reset */
+ FME(1)); /* FIFO mode enable */
+
+ /* IR mode configuration */
+ jz_overwritef(UART_ISR(2),
+ RDPL(1), /* Zero is negative pulse for receive */
+ TDPL(1), /* ... and for transmit */
+ XMODE(1), /* Pulse width 1.6us */
+ RCVEIR(0), /* Disable IR for recieve */
+ XMITIR(0)); /* ... and for transmit */
+
+ /* Line configuration */
+ jz_overwritef(UART_ULCR(2), DLAB(0),
+ WLS_V(8BITS), /* 8 bit words */
+ SBLS_V(1_STOP_BIT), /* 1 stop bit */
+ PARE(0), /* no parity */
+ SBK(0)); /* don't set break */
+
+ /* Set the baud rate... not too sure how this works. (Docs unclear!) */
+ const unsigned divisor = 0x0004;
+ jz_writef(UART_ULCR(2), DLAB(1));
+ jz_write(UART_UDLHR(2), (divisor >> 8) & 0xff);
+ jz_write(UART_UDLLR(2), divisor & 0xff);
+ jz_write(UART_UMR(2), 16);
+ jz_write(UART_UACR(2), 0);
+ jz_writef(UART_ULCR(2), DLAB(0));
+
+ /* Enable UART */
+ jz_overwritef(UART_UFCR(2),
+ RDTR(0), /* FIFO trigger level = 1 */
+ DME(0), /* DMA mode disable */
+ UME(1), /* UART module enable */
+ TFRT(1), /* transmit FIFO reset */
+ RFRT(1), /* receive FIFO reset */
+ FME(1)); /* FIFO mode enable */
+}
+
+int x1000_dualboot_load_pdma_fw(void)
+{
+ struct nand_drv* n = nand_init();
+ nand_lock(n);
+
+ int ret = nand_open(n);
+ if(ret != NAND_SUCCESS)
+ goto err_unlock;
+
+ /* NOTE: hardcoded address is used by all current targets */
+ jz_writef(CPM_CLKGR, PDMA(0));
+ ret = nand_read_bytes(n, 0x4000, 0x2000, (void*)0xb3422000);
+
+ nand_close(n);
+ err_unlock:
+ nand_unlock(n);
+ return ret;
+}
diff --git a/firmware/target/mips/ingenic_x1000/boot-x1000.h b/firmware/target/mips/ingenic_x1000/boot-x1000.h
new file mode 100644
index 0000000000..eb476c513d
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/boot-x1000.h
@@ -0,0 +1,81 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef __BOOT_X1000_H__
+#define __BOOT_X1000_H__
+
+#include "x1000/cpm.h"
+#include <stdbool.h>
+#include <stdint.h>
+#include <stddef.h>
+
+enum {
+ /* Set after running clk_init() and setting up system clocks */
+ BOOT_FLAG_CLK_INIT = (1 << 31),
+
+ /* Set by the SPL if it was loaded over USB boot */
+ BOOT_FLAG_USB_BOOT = (1 << 30),
+};
+
+void x1000_boot_rockbox(const void* source, size_t length)
+ __attribute__((section(".icode.x1000_boot_rockbox")));
+void x1000_boot_linux(const void* source, size_t length,
+ void* load, void* entry, const char* args)
+ __attribute__((section(".icode.x1000_boot_linux")));
+
+/* dual boot support code */
+void x1000_dualboot_cleanup(void);
+void x1000_dualboot_init_clocktree(void);
+void x1000_dualboot_init_uart2(void);
+int x1000_dualboot_load_pdma_fw(void);
+
+/* Note: these functions are inlined to minimize SPL code size.
+ * They are private to the X1000 early boot code anyway... */
+
+static inline void cpm_scratch_set(uint32_t value)
+{
+ /* TODO: see if this holds its state over a WDT reset */
+ REG_CPM_SCRATCH_PROT = 0x5a5a;
+ REG_CPM_SCRATCH = value;
+ REG_CPM_SCRATCH_PROT = 0xa5a5;
+}
+
+static inline void init_boot_flags(void)
+{
+ cpm_scratch_set(0);
+}
+
+static inline bool get_boot_flag(uint32_t bit)
+{
+ return (REG_CPM_SCRATCH & bit) != 0;
+}
+
+static inline void set_boot_flag(uint32_t bit)
+{
+ cpm_scratch_set(REG_CPM_SCRATCH | bit);
+}
+
+static inline void clr_boot_flag(uint32_t bit)
+{
+ cpm_scratch_set(REG_CPM_SCRATCH & ~bit);
+}
+
+#endif /* __BOOT_X1000_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/boot.lds b/firmware/target/mips/ingenic_x1000/boot.lds
index 81468a95fc..c274d69aab 100644
--- a/firmware/target/mips/ingenic_x1000/boot.lds
+++ b/firmware/target/mips/ingenic_x1000/boot.lds
@@ -1,5 +1 @@
-#ifdef BOOTLOADER_SPL
-# include "spl.lds"
-#else
-# include "app.lds"
-#endif
+#include "app.lds"
diff --git a/firmware/target/mips/ingenic_x1000/clk-x1000.c b/firmware/target/mips/ingenic_x1000/clk-x1000.c
index 390d9722ac..e3b0f792bb 100644
--- a/firmware/target/mips/ingenic_x1000/clk-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/clk-x1000.c
@@ -21,103 +21,134 @@
#include "system.h"
#include "clk-x1000.h"
+#include "boot-x1000.h"
#include "x1000/cpm.h"
#include "x1000/msc.h"
#include "x1000/aic.h"
-static uint32_t pll_get(uint32_t pllreg, uint32_t onbit)
+struct clk_info {
+ uint8_t mux_reg; /* offset from CPM_BASE for mux register */
+ uint8_t mux_shift; /* shift to get mux */
+ uint8_t mux_mask; /* mask to get mux after shifting */
+ uint8_t mux_type; /* type of mux, maps register bits to clock source */
+ uint8_t div_reg; /* offset from CPM_BASE for divider register */
+ uint8_t div_shift; /* shift to get divider */
+ uint8_t div_mask; /* mask to get divider after shifting */
+ uint8_t miscbits; /* inclk shift, clkgr bit, and fake mux value */
+};
+
+/* Ugliness to pack/unpack stuff in clk_info->miscbits */
+#define INCLK_SHIFT(n) ((n) & 1)
+#define CLKGR_BIT(n) (((n) & 0x1f) << 1)
+#define FAKEMUX(n) (((n) & 3) << 6)
+#define GET_INCLK_SHIFT(miscbits) ((miscbits) & 1)
+#define GET_CLKGR_BIT(miscbits) (((miscbits) >> 1) & 0x1f)
+#define GET_FAKEMUX(miscbits) (((miscbits) >> 6) & 3)
+
+/* Clock sources -- the order here is important! */
+#define S_STOP 0
+#define S_EXCLK 1
+#define S_APLL 2
+#define S_MPLL 3
+#define S_SCLK_A 4
+
+/* Muxes */
+#define MUX_TWOBIT 0
+#define MUX_ONEBIT 1
+#define MUX_USB 2
+#define MUX_PHONY 3
+#define MUX_NUM_TYPES 4
+
+/* Ugliness to define muxes */
+#define MKSEL(x,i) (((x) & 0xf) << ((i)*4))
+#define GETSEL(x,i) (((x) >> ((i)*4)) & 0xf)
+#define STOP(i) MKSEL(S_STOP, i)
+#define EXCLK(i) MKSEL(S_EXCLK, i)
+#define APLL(i) MKSEL(S_APLL, i)
+#define MPLL(i) MKSEL(S_MPLL, i)
+#define SCLK_A(i) MKSEL(S_SCLK_A, i)
+#define MKMUX(a,b,c,d) (a(0)|b(1)|c(2)|d(3))
+
+/* Ugliness to shorten the clk_info table */
+#define JA(x) (JA_CPM_##x & 0xff)
+#define BM(x) ((BM_CPM_##x) >> (BP_CPM_##x))
+#define BP(x) (BP_CPM_##x)
+#define CG(x) CLKGR_BIT(BP_CPM_CLKGR_##x)
+#define M(r,f,t) JA(r), BP(r##_##f), BM(r##_##f), t
+#define D(r,f) JA(r), BP(r##_##f), BM(r##_##f)
+
+static const uint16_t clk_mux[MUX_NUM_TYPES] = {
+ /* 00 01 10 11 */
+ MKMUX(STOP, SCLK_A, MPLL, STOP), /* MUX_TWOBIT */
+ MKMUX(SCLK_A, MPLL, STOP, STOP), /* MUX_ONEBIT */
+ MKMUX(EXCLK, EXCLK, SCLK_A, MPLL), /* MUX_USB */
+ MKMUX(EXCLK, APLL, MPLL, SCLK_A), /* MUX_PHONY */
+};
+
+/* Keep in order with enum x1000_clk_t */
+const struct clk_info clk_info[X1000_NUM_SIMPLE_CLKS] = {
+ {0, 0, 0, MUX_PHONY, 0, 0, 0, CG(CPU_BIT)|FAKEMUX(0)},
+ {0, 0, 0, MUX_PHONY, 0, 0, 0, CG(CPU_BIT)|FAKEMUX(1)},
+ {0, 0, 0, MUX_PHONY, 0, 0, 0, CG(CPU_BIT)|FAKEMUX(2)},
+ {0, 0, 0, MUX_PHONY, 0, 0, 0, CG(CPU_BIT)|FAKEMUX(3)},
+ {M(CCR, SEL_CPLL, MUX_TWOBIT), D(CCR, CDIV), CG(CPU_BIT)},
+ {M(CCR, SEL_CPLL, MUX_TWOBIT), D(CCR, L2DIV), CG(CPU_BIT)},
+ {M(CCR, SEL_H0PLL, MUX_TWOBIT), D(CCR, H0DIV), CG(AHB0)},
+ {M(CCR, SEL_H2PLL, MUX_TWOBIT), D(CCR, H2DIV), CG(APB0)},
+ {M(CCR, SEL_H2PLL, MUX_TWOBIT), D(CCR, PDIV), CG(APB0)},
+ {M(DDRCDR, CLKSRC, MUX_TWOBIT), D(DDRCDR, CLKDIV), CG(DDR)},
+ {M(LPCDR, CLKSRC, MUX_ONEBIT), D(LPCDR, CLKDIV), CG(LCD)},
+ {M(MSC0CDR, CLKSRC, MUX_ONEBIT), D(MSC0CDR, CLKDIV), CG(MSC0)|INCLK_SHIFT(1)},
+ {M(MSC0CDR, CLKSRC, MUX_ONEBIT), D(MSC1CDR, CLKDIV), CG(MSC1)|INCLK_SHIFT(1)},
+ {M(SSICDR, SFC_CS, MUX_ONEBIT), D(SSICDR, CLKDIV), CG(SFC)},
+ {M(USBCDR, CLKSRC, MUX_USB), D(USBCDR, CLKDIV), CG(OTG)},
+};
+
+static uint32_t clk_get_in_rate(uint8_t mux_type, uint32_t mux)
{
- if((pllreg & (1 << onbit)) == 0)
- return 0;
-
- /* Both PLL registers share the same layout of N/M/OD bits.
- * The max multiplier is 128 and max EXCLK is 26 MHz, so the
- * multiplication should fit within 32 bits without overflow.
- */
- uint32_t rate = X1000_EXCLK_FREQ;
- rate *= jz_vreadf(pllreg, CPM_APCR, PLLM) + 1;
- rate /= jz_vreadf(pllreg, CPM_APCR, PLLN) + 1;
- rate >>= jz_vreadf(pllreg, CPM_APCR, PLLOD);
- return rate;
-}
-
-static uint32_t sclk_a_get(void)
-{
- switch(jz_readf(CPM_CCR, SEL_SRC)) {
- case 1: return X1000_EXCLK_FREQ;
- case 2: return clk_get(X1000_CLK_APLL);
- default: return 0;
- }
-}
-
-static uint32_t ccr_get(uint32_t selbit, uint32_t divbit)
-{
- uint32_t reg = REG_CPM_CCR;
- uint32_t sel = (reg >> selbit) & 0x3;
- uint32_t div = (reg >> divbit) & 0xf;
-
- switch(sel) {
- case 1: return clk_get(X1000_CLK_SCLK_A) / (div + 1);
- case 2: return clk_get(X1000_CLK_MPLL) / (div + 1);
- default: return 0;
- }
-}
-
-static uint32_t ddr_get(void)
-{
- uint32_t reg = REG_CPM_DDRCDR;
- uint32_t div = jz_vreadf(reg, CPM_DDRCDR, CLKDIV);
-
- switch(jz_vreadf(reg, CPM_DDRCDR, CLKSRC)) {
- case 1: return clk_get(X1000_CLK_SCLK_A) / (div + 1);
- case 2: return clk_get(X1000_CLK_MPLL) / (div + 1);
- default: return 0;
+ uint32_t reg, onbit;
+ uint32_t src = GETSEL(clk_mux[mux_type], mux);
+ again:
+ switch(src) {
+ default: return 0;
+ case S_EXCLK: return X1000_EXCLK_FREQ;
+ case S_APLL: reg = REG_CPM_APCR; onbit = BM_CPM_APCR_ON; break;
+ case S_MPLL: reg = REG_CPM_MPCR; onbit = BM_CPM_MPCR_ON; break;
+ case S_SCLK_A: src = jz_readf(CPM_CCR, SEL_SRC); goto again;
}
-}
-static uint32_t lcd_get(void)
-{
- if(jz_readf(CPM_CLKGR, LCD))
+ if(!(reg & onbit))
return 0;
- uint32_t reg = REG_CPM_LPCDR;
- uint32_t rate;
- switch(jz_vreadf(reg, CPM_LPCDR, CLKSRC)) {
- case 0: rate = clk_get(X1000_CLK_SCLK_A); break;
- case 1: rate = clk_get(X1000_CLK_MPLL); break;
- default: return 0;
- }
-
- rate /= jz_vreadf(reg, CPM_LPCDR, CLKDIV) + 1;
+ uint32_t rate = X1000_EXCLK_FREQ;
+ rate *= jz_vreadf(reg, CPM_APCR, PLLM) + 1;
+ rate /= jz_vreadf(reg, CPM_APCR, PLLN) + 1;
+ rate >>= jz_vreadf(reg, CPM_APCR, PLLOD);
return rate;
}
-static uint32_t msc_get(int msc)
+static uint32_t clk_get_simple(const struct clk_info* info)
{
- if((msc == 0 && jz_readf(CPM_CLKGR, MSC0)) ||
- (msc == 1 && jz_readf(CPM_CLKGR, MSC1)))
+ if(REG_CPM_CLKGR & (1 << GET_CLKGR_BIT(info->miscbits)))
return 0;
- uint32_t reg = REG_CPM_MSC0CDR;
- uint32_t rate;
- switch(jz_vreadf(reg, CPM_MSC0CDR, CLKSRC)) {
- case 0: rate = clk_get(X1000_CLK_SCLK_A); break;
- case 1: rate = clk_get(X1000_CLK_MPLL); break;
- default: return 0;
- }
+ uint32_t base = 0xb0000000;
+ uint32_t mux_reg = *(const volatile uint32_t*)(base + info->mux_reg);
+ uint32_t div_reg = *(const volatile uint32_t*)(base + info->div_reg);
- uint32_t div;
- if(msc == 0)
- div = jz_readf(CPM_MSC0CDR, CLKDIV);
- else
- div = jz_readf(CPM_MSC1CDR, CLKDIV);
+ uint32_t mux = (mux_reg >> info->mux_shift) & info->mux_mask;
+ uint32_t div = (div_reg >> info->div_shift) & info->div_mask;
- rate /= 2 * (div + 1);
- rate >>= REG_MSC_CLKRT(msc);
+ mux |= GET_FAKEMUX(info->miscbits);
+
+ uint32_t rate = clk_get_in_rate(info->mux_type, mux);
+ rate >>= GET_INCLK_SHIFT(info->miscbits);
+ rate /= (div + 1);
return rate;
}
-static uint32_t i2s_mclk_get(void)
+#ifndef BOOTLOADER
+static uint32_t clk_get_i2s_mclk(void)
{
if(jz_readf(CPM_CLKGR, AIC))
return 0;
@@ -143,48 +174,29 @@ static uint32_t i2s_mclk_get(void)
return rate;
}
-static uint32_t i2s_bclk_get(void)
+static uint32_t clk_get_i2s_bclk(void)
{
- return i2s_mclk_get() / (REG_AIC_I2SDIV + 1);
+ return clk_get_i2s_mclk() / (REG_AIC_I2SDIV + 1);
}
-static uint32_t sfc_get(void)
+static uint32_t clk_get_decimal(x1000_clk_t clk)
{
- if(jz_readf(CPM_CLKGR, SFC))
- return 0;
-
- uint32_t reg = REG_CPM_SSICDR;
- uint32_t rate;
- if(jz_vreadf(reg, CPM_SSICDR, SFC_CS) == 0)
- rate = clk_get(X1000_CLK_SCLK_A);
- else
- rate = clk_get(X1000_CLK_MPLL);
-
- rate /= jz_vreadf(reg, CPM_SSICDR, CLKDIV) + 1;
- return rate;
+ switch(clk) {
+ case X1000_CLK_I2S_MCLK: return clk_get_i2s_mclk();
+ case X1000_CLK_I2S_BCLK: return clk_get_i2s_bclk();
+ default: return 0;
+ }
}
+#endif
uint32_t clk_get(x1000_clk_t clk)
{
- switch(clk) {
- case X1000_CLK_EXCLK: return X1000_EXCLK_FREQ;
- case X1000_CLK_APLL: return pll_get(REG_CPM_APCR, BP_CPM_APCR_ON);
- case X1000_CLK_MPLL: return pll_get(REG_CPM_MPCR, BP_CPM_MPCR_ON);
- case X1000_CLK_SCLK_A: return sclk_a_get();
- case X1000_CLK_CPU: return ccr_get(BP_CPM_CCR_SEL_CPLL, BP_CPM_CCR_CDIV);
- case X1000_CLK_L2CACHE: return ccr_get(BP_CPM_CCR_SEL_CPLL, BP_CPM_CCR_L2DIV);
- case X1000_CLK_AHB0: return ccr_get(BP_CPM_CCR_SEL_H0PLL, BP_CPM_CCR_H0DIV);
- case X1000_CLK_AHB2: return ccr_get(BP_CPM_CCR_SEL_H2PLL, BP_CPM_CCR_H2DIV);
- case X1000_CLK_PCLK: return ccr_get(BP_CPM_CCR_SEL_H2PLL, BP_CPM_CCR_PDIV);
- case X1000_CLK_DDR: return ddr_get();
- case X1000_CLK_LCD: return lcd_get();
- case X1000_CLK_MSC0: return msc_get(0);
- case X1000_CLK_MSC1: return msc_get(1);
- case X1000_CLK_I2S_MCLK: return i2s_mclk_get();
- case X1000_CLK_I2S_BCLK: return i2s_bclk_get();
- case X1000_CLK_SFC: return sfc_get();
- default: return 0;
- }
+#ifndef BOOTLOADER
+ if(clk >= X1000_NUM_SIMPLE_CLKS)
+ return clk_get_decimal(clk);
+#endif
+
+ return clk_get_simple(&clk_info[clk]);
}
const char* clk_get_name(x1000_clk_t clk)
@@ -204,16 +216,96 @@ const char* clk_get_name(x1000_clk_t clk)
CASE(LCD);
CASE(MSC0);
CASE(MSC1);
+ CASE(SFC);
+ CASE(USB);
CASE(I2S_MCLK);
CASE(I2S_BCLK);
- CASE(SFC);
#undef CASE
default:
return "NONE";
}
}
+/* At present we've only got 24 MHz targets, and they are all using
+ * the same "standard" configuration. */
+#if X1000_EXCLK_FREQ == 24000000
+void clk_init_early(void)
+{
+ jz_writef(CPM_MPCR, ENABLE(0));
+ while(jz_readf(CPM_MPCR, ON));
+
+ /* 24 MHz * 25 = 600 MHz */
+ jz_writef(CPM_MPCR, BS(1), PLLM(25 - 1), PLLN(0), PLLOD(0), ENABLE(1));
+ while(jz_readf(CPM_MPCR, ON) == 0);
+
+ /* 600 MHz / 3 = 200 MHz */
+ clk_set_ddr(X1000_CLK_MPLL, 3);
+}
+
+void clk_init(void)
+{
+ /* make sure we only initialize the clocks once */
+ if(get_boot_flag(BOOT_FLAG_CLK_INIT))
+ return;
+
+ clk_set_ccr_mux(CLKMUX_SCLK_A(EXCLK) |
+ CLKMUX_CPU(SCLK_A) |
+ CLKMUX_AHB0(SCLK_A) |
+ CLKMUX_AHB2(SCLK_A));
+ clk_set_ccr_div(CLKDIV_CPU(1) |
+ CLKDIV_L2(1) |
+ CLKDIV_AHB0(1) |
+ CLKDIV_AHB2(1) |
+ CLKDIV_PCLK(1));
+
+ jz_writef(CPM_APCR, ENABLE(0));
+ while(jz_readf(CPM_APCR, ON));
+
+ /* 24 MHz * 42 = 1008 MHz */
+ jz_writef(CPM_APCR, BS(1), PLLM(42 - 1), PLLN(0), PLLOD(0), ENABLE(1));
+ while(jz_readf(CPM_APCR, ON) == 0);
+
+#if defined(FIIO_M3K) || defined(EROS_QN)
+ /* TODO: Allow targets to define their clock frequencies in their config,
+ * instead of having this be a random special case. */
+ clk_set_ccr_div(CLKDIV_CPU(1) | /* 1008 MHz */
+ CLKDIV_L2(2) | /* 504 MHz */
+ CLKDIV_AHB0(5) | /* 201.6 MHz */
+ CLKDIV_AHB2(5) | /* 201.6 MHz */
+ CLKDIV_PCLK(10)); /* 100.8 MHz */
+ clk_set_ccr_mux(CLKMUX_SCLK_A(APLL) |
+ CLKMUX_CPU(SCLK_A) |
+ CLKMUX_AHB0(SCLK_A) |
+ CLKMUX_AHB2(SCLK_A));
+
+ /* DDR to 201.6 MHz */
+ clk_set_ddr(X1000_CLK_SCLK_A, 5);
+
+ /* Disable MPLL */
+ jz_writef(CPM_MPCR, ENABLE(0));
+ while(jz_readf(CPM_MPCR, ON));
+#else
+ /* Default configuration matching the Ingenic OF */
+ clk_set_ccr_div(CLKDIV_CPU(1) | /* 1008 MHz */
+ CLKDIV_L2(2) | /* 504 MHz */
+ CLKDIV_AHB0(3) | /* 200 MHz */
+ CLKDIV_AHB2(3) | /* 200 MHz */
+ CLKDIV_PCLK(6)); /* 100 MHz */
+ clk_set_ccr_mux(CLKMUX_SCLK_A(APLL) |
+ CLKMUX_CPU(SCLK_A) |
+ CLKMUX_AHB0(MPLL) |
+ CLKMUX_AHB2(MPLL));
+#endif
+
+ /* mark that clocks have been initialized */
+ set_boot_flag(BOOT_FLAG_CLK_INIT);
+}
+#else
+# error "please write a new clk_init() for this EXCLK frequency"
+#endif
+
#define CCR_MUX_BITS jz_orm(CPM_CCR, SEL_SRC, SEL_CPLL, SEL_H0PLL, SEL_H2PLL)
+#define CCR_DIV_BITS jz_orm(CPM_CCR, CDIV, L2DIV, H0DIV, H2DIV, PDIV)
#define CSR_MUX_BITS jz_orm(CPM_CSR, SRC_MUX, CPU_MUX, AHB0_MUX, AHB2_MUX)
#define CSR_DIV_BITS jz_orm(CPM_CSR, H2DIV_BUSY, H0DIV_BUSY, CDIV_BUSY)
@@ -229,12 +321,14 @@ void clk_set_ccr_mux(uint32_t muxbits)
while((REG_CPM_CSR & CSR_MUX_BITS) != CSR_MUX_BITS);
}
-void clk_set_ccr_div(int cpu, int l2, int ahb0, int ahb2, int pclk)
+void clk_set_ccr_div(uint32_t divbits)
{
/* Set new divider configuration */
- jz_writef(CPM_CCR, CDIV(cpu - 1), L2DIV(l2 - 1),
- H0DIV(ahb0 - 1), H2DIV(ahb2 - 1), PDIV(pclk - 1),
- CE_CPU(1), CE_AHB0(1), CE_AHB2(1));
+ uint32_t reg = REG_CPM_CCR;
+ reg &= ~CCR_DIV_BITS;
+ reg |= divbits & CCR_DIV_BITS;
+ reg |= jz_orm(CPM_CCR, CE_CPU, CE_AHB0, CE_AHB2);
+ REG_CPM_CCR = reg;
/* Wait until divider change completes */
while(REG_CPM_CSR & CSR_DIV_BITS);
diff --git a/firmware/target/mips/ingenic_x1000/clk-x1000.h b/firmware/target/mips/ingenic_x1000/clk-x1000.h
index 76413b90d2..4b1d4ef838 100644
--- a/firmware/target/mips/ingenic_x1000/clk-x1000.h
+++ b/firmware/target/mips/ingenic_x1000/clk-x1000.h
@@ -22,8 +22,9 @@
#ifndef __CLK_X1000_H__
#define __CLK_X1000_H__
-#include <stdint.h>
+#include "config.h"
#include "x1000/cpm.h"
+#include <stdint.h>
/* Used as arguments to clk_set_ccr_mux() */
#define CLKMUX_SCLK_A(x) jz_orf(CPM_CCR, SEL_SRC_V(x))
@@ -31,6 +32,13 @@
#define CLKMUX_AHB0(x) jz_orf(CPM_CCR, SEL_H0PLL_V(x))
#define CLKMUX_AHB2(x) jz_orf(CPM_CCR, SEL_H2PLL_V(x))
+/* Arguments to clk_set_ccr_div() */
+#define CLKDIV_CPU(x) jz_orf(CPM_CCR, CDIV((x) - 1))
+#define CLKDIV_L2(x) jz_orf(CPM_CCR, L2DIV((x) - 1))
+#define CLKDIV_AHB0(x) jz_orf(CPM_CCR, H0DIV((x) - 1))
+#define CLKDIV_AHB2(x) jz_orf(CPM_CCR, H2DIV((x) - 1))
+#define CLKDIV_PCLK(x) jz_orf(CPM_CCR, PDIV((x) - 1))
+
typedef enum x1000_clk_t {
X1000_CLK_EXCLK,
X1000_CLK_APLL,
@@ -45,9 +53,11 @@ typedef enum x1000_clk_t {
X1000_CLK_LCD,
X1000_CLK_MSC0,
X1000_CLK_MSC1,
- X1000_CLK_I2S_MCLK,
- X1000_CLK_I2S_BCLK,
X1000_CLK_SFC,
+ X1000_CLK_USB,
+ X1000_NUM_SIMPLE_CLKS,
+ X1000_CLK_I2S_MCLK = X1000_NUM_SIMPLE_CLKS,
+ X1000_CLK_I2S_BCLK,
X1000_CLK_COUNT,
} x1000_clk_t;
@@ -57,23 +67,27 @@ extern uint32_t clk_get(x1000_clk_t clk);
/* Get the name of a clock for debug purposes */
extern const char* clk_get_name(x1000_clk_t clk);
+/* Clock initialization */
+extern void clk_init_early(void) INIT_ATTR;
+extern void clk_init(void) INIT_ATTR;
+
/* Sets system clock multiplexers */
-extern void clk_set_ccr_mux(uint32_t muxbits);
+extern void clk_set_ccr_mux(uint32_t muxbits) INIT_ATTR;
/* Sets system clock dividers */
-extern void clk_set_ccr_div(int cpu, int l2, int ahb0, int ahb2, int pclk);
+extern void clk_set_ccr_div(uint32_t divbits) INIT_ATTR;
/* Sets DDR clock source and divider */
-extern void clk_set_ddr(x1000_clk_t src, uint32_t div);
+extern void clk_set_ddr(x1000_clk_t src, uint32_t div) INIT_ATTR;
/* Returns the smallest n such that infreq/n <= outfreq */
-inline uint32_t clk_calc_div(uint32_t infreq, uint32_t outfreq)
+static inline uint32_t clk_calc_div(uint32_t infreq, uint32_t outfreq)
{
return (infreq + (outfreq - 1)) / outfreq;
}
/* Returns the smallest n such that (infreq >> n) <= outfreq */
-inline uint32_t clk_calc_shift(uint32_t infreq, uint32_t outfreq)
+static inline uint32_t clk_calc_shift(uint32_t infreq, uint32_t outfreq)
{
uint32_t div = clk_calc_div(infreq, outfreq);
return __builtin_clz(div) ^ 31;
diff --git a/firmware/target/mips/ingenic_x1000/crt0.S b/firmware/target/mips/ingenic_x1000/crt0.S
index b717f96692..6c0942b0db 100644
--- a/firmware/target/mips/ingenic_x1000/crt0.S
+++ b/firmware/target/mips/ingenic_x1000/crt0.S
@@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Copyright (C) 2021 Aidan MacDonald
+ * Copyright (C) 2021-2022 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -21,9 +21,12 @@
#include "config.h"
#include "mips.h"
+#include "bootdata.h"
.text
.extern main
+ .extern system_early_init
+ .extern _loadaddress
.global _start
.set push
@@ -31,235 +34,126 @@
.set noreorder
.set noat
- .section .init.text
+ .section .startup.text,"ax",%progbits
_start:
- /* Clear data watchpoint */
- mtc0 zero, C0_WATCHLO
- mtc0 zero, C0_WATCHHI
+ b _realstart
+ nop
- /* Set BEV, ERL, mask interrupts */
- li v0, 0x40fc04
- mtc0 v0, C0_Status
+ /* Header entries are 4-byte string labels (not null terminated!) followed
+ * by 4-byte values. Header should begin in the first 128 bytes and should
+ * be no more than 256 bytes in length. */
+_header:
+ .ascii "BEGINHDR" /* beginning of header */
+ .ascii "LOAD"
+ .word _loadaddress
+ .ascii "ENDH" /* end of header structure */
- /* Set Cause_IV to 1 (use special interrupt vector) */
- li v0, M_CauseIV
- mtc0 v0, C0_Cause
+#ifndef BOOTLOADER
+ /* Multiboot support header; this is not part of the above header. */
+ put_boot_data_here
+#endif
- /* Set CPU_MODE and BUS_MODE to 1 in CPM_OPCR (Ingenic does this) */
- lui v0, 0xb000
- lw v1, 0x24(v0)
- ori v1, v1, 0x22
- sw v1, 0x24(v0)
+_realstart:
+ /* Save bootloader arguments. */
+ move s0, a0
+ move s1, a1
+ move s2, a2
+ move s3, a3
- /* Enable kseg0 cacheability */
- li v0, 3
- mtc0 v0, C0_Config
+ /* Copy IRAM from BSS to low memory. */
+ la a0, _iramcopy
+ la a1, _iramstart
+ la a2, _iramend
+ bal _copy
nop
- /* According to ingenic: "enable idx-store-data cache insn" */
- li v0, 0x20000000
- mtc0 v0, C0_ErrCtl
-
- /* Cache init */
- li v0, 0x80000000
- ori v1, v0, 0x4000
- mtc0 zero, C0_TAGLO
- mtc0 zero, C0_TAGHI
-_cache_loop:
- cache ICIndexStTag, 0(v0)
- cache DCIndexStTag, 0(v0)
- addiu v0, v0, 32
- bne v0, v1, _cache_loop
+ /* Copy TCSM from BSS */
+ la a0, _tcsmcopy
+ la a1, _tcsmstart
+ la a2, _tcsmend
+ bal _copy
nop
- /* Invalidate BTB */
- mfc0 v0, C0_Config, 7
+#ifdef HAVE_INIT_ATTR
+ /* Copy init code */
+ la a0, _initcopy
+ la a1, _initstart
+ la a2, _initend
+ bal _copy
nop
- ori v0, v0, 2
- mtc0 v0, C0_Config, 7
- nop
-
-#ifndef BOOTLOADER_SPL
- /* Copy IRAM from BSS to low memory. */
- la t0, _iramcopy
- la t1, _iramstart
- la t2, _iramend
-_iram_loop:
- lw t3, 0(t0)
- addiu t1, 4
- addiu t0, 4
- bne t1, t2, _iram_loop
- sw t3, -4(t1)
#endif
/* Clear the BSS segment (needed to zero-initialize C static values) */
- la t0, _bssbegin
- la t1, _bssend
- beq t0, t1, _bss_done
-_bss_loop:
- addiu t0, 4
- bne t0, t1, _bss_loop
- sw zero, -4(t0)
-_bss_done:
+ la a0, _bssbegin
+ la a1, _bssend
+ bal _clear
+ move a2, $0
-#ifndef BOOTLOADER_SPL
/* Set stack pointer and clear the stack */
la sp, stackend
- la t0, stackbegin
- li t1, 0xDEADBEEF
-_stack_loop:
- addiu t0, 4
- bne t0, sp, _stack_loop
- sw t1, -4(t0)
+ la a0, stackbegin
+ li a2, 0xDEADBEEF
+ bal _clear
+ move a1, sp
/* Clear the IRQ stack */
la k0, _irqstackend
- la t0, _irqstackbegin
-_irqstack_loop:
- addiu t0, 4
- bne t0, k0, _irqstack_loop
- sw t1, -4(t0)
-#endif
-
- /* Jump to C code */
- j main
- nop
-
-#ifndef BOOTLOADER_SPL
- /* Exception entry points */
- .section .vectors.1, "ax", %progbits
- j tlb_refill_handler
- nop
-
- .section .vectors.2, "ax", %progbits
- j real_exception_handler
- nop
+ la a0, _irqstackbegin
+ bal _clear
+ move a1, k0
- .section .vectors.3, "ax", %progbits
- j real_exception_handler
- nop
-
- .section .vectors.4, "ax", %progbits
- j real_exception_handler
- nop
+ /* Write back D-cache and invalidate I-cache */
+ li v0, 0x80000000
+ ori v1, v0, (0x4000 - 32)
+ mtc0 zero, C0_TAGLO
+ mtc0 zero, C0_TAGHI
+1:
+ cache DCIndexWBInv, 0(v0)
+ cache ICIndexStTag, 0(v0)
+ bne v0, v1, 1b
+ addiu v0, v0, 32
- .section .vectors, "ax", %progbits
-real_exception_handler:
- move k0, sp
- la sp, _irqstackend
- addiu sp, -0x84
- sw k0, 0x80(sp)
- sw ra, 0x00(sp)
- sw fp, 0x04(sp)
- sw gp, 0x08(sp)
- sw t9, 0x0c(sp)
- sw t8, 0x10(sp)
- sw s7, 0x14(sp)
- sw s6, 0x18(sp)
- sw s5, 0x1c(sp)
- sw s4, 0x20(sp)
- sw s3, 0x24(sp)
- sw s2, 0x28(sp)
- sw s1, 0x2c(sp)
- sw s0, 0x30(sp)
- sw t7, 0x34(sp)
- sw t6, 0x38(sp)
- sw t5, 0x3c(sp)
- sw t4, 0x40(sp)
- sw t3, 0x44(sp)
- sw t2, 0x48(sp)
- sw t1, 0x4c(sp)
- sw t0, 0x50(sp)
- sw a3, 0x54(sp)
- sw a2, 0x58(sp)
- sw a1, 0x5c(sp)
- sw a0, 0x60(sp)
- sw v1, 0x64(sp)
- sw v0, 0x68(sp)
- sw $1, 0x6c(sp)
- mflo k0
- nop
- sw k0, 0x70(sp)
- mfhi k0
- nop
- sw k0, 0x74(sp)
- mfc0 k0, C0_STATUS
- nop
- nop
- nop
- sw k0, 0x78(sp)
- mfc0 k0, C0_EPC
- nop
+ /* Invalidate BTB */
+ mfc0 v0, C0_Config, 7
nop
+ ori v0, v0, 2
+ mtc0 v0, C0_Config, 7
nop
- sw k0, 0x7c(sp)
- li k1, M_CauseExcCode
- mfc0 a0, C0_CAUSE
- and k0, a0, k1
- bnez k0, _exception
- nop
- jal intr_handler
+ /* Jump to C code */
+ jal system_early_init
nop
- j _exception_return
-_exception:
- mfc0 a1, C0_EPC
- nop
- nop
- nop
- jal exception_handler
- move a2, sp
+ /* Restore bootloader arguments, jump to main. */
+ move a0, s0
+ move a1, s1
+ move a2, s2
+ move a3, s3
-_exception_return:
- lw ra, 0x00(sp)
- lw fp, 0x04(sp)
- lw gp, 0x08(sp)
- lw t9, 0x0c(sp)
- lw t8, 0x10(sp)
- lw s7, 0x14(sp)
- lw s6, 0x18(sp)
- lw s5, 0x1c(sp)
- lw s4, 0x20(sp)
- lw s3, 0x24(sp)
- lw s2, 0x28(sp)
- lw s1, 0x2c(sp)
- lw s0, 0x30(sp)
- lw t7, 0x34(sp)
- lw t6, 0x38(sp)
- lw t5, 0x3c(sp)
- lw t4, 0x40(sp)
- lw t3, 0x44(sp)
- lw t2, 0x48(sp)
- lw t1, 0x4c(sp)
- lw t0, 0x50(sp)
- lw a3, 0x54(sp)
- lw a2, 0x58(sp)
- lw a1, 0x5c(sp)
- lw a0, 0x60(sp)
- lw v1, 0x64(sp)
- lw v0, 0x68(sp)
- lw $1, 0x6c(sp)
- lw k0, 0x70(sp)
- mtlo k0
- nop
- lw k0, 0x74(sp)
- mthi k0
- nop
- lw k0, 0x78(sp)
- mtc0 k0, C0_STATUS
- nop
- nop
- nop
- lw k0, 0x7c(sp)
- mtc0 k0, C0_EPC
- nop
- nop
- nop
- lw sp, 0x80(sp)
- eret
+ j main
+ move ra, zero /* init backtrace root */
+
+ /* copy(void* src, void* dst, void* dst_end) */
+_copy:
+ beq a1, a2, 1f
+ addiu a1, 4
+ lw t0, 0(a0)
+ addiu a0, 4
+ b _copy
+ sw t0, -4(a1)
+1:
+ jr ra
+ nop
+
+ /* clear(void* dst, void* dst_end, int value) */
+_clear:
+ beq a0, a1, 1f
+ addiu a0, 4
+ b _clear
+ sw a2, -4(a0)
+1:
+ jr ra
nop
-#endif
.set pop
diff --git a/firmware/target/mips/ingenic_x1000/debug-x1000.c b/firmware/target/mips/ingenic_x1000/debug-x1000.c
index fed586691c..827bb37855 100644
--- a/firmware/target/mips/ingenic_x1000/debug-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/debug-x1000.c
@@ -118,18 +118,38 @@ static bool dbg_gpios(void)
}
extern volatile unsigned aic_tx_underruns;
+#ifdef HAVE_RECORDING
+extern volatile unsigned aic_rx_overruns;
+#endif
+#ifdef HAVE_EROS_QN_CODEC
+extern int es9018k2m_present_flag;
+#endif
static bool dbg_audio(void)
{
do {
lcd_clear_display();
lcd_putsf(0, 0, "TX underruns: %u", aic_tx_underruns);
+#ifdef HAVE_RECORDING
+ lcd_putsf(0, 1, "RX overruns: %u", aic_rx_overruns);
+#endif
+#ifdef HAVE_EROS_QN_CODEC
+ if (es9018k2m_present_flag)
+ {
+ lcd_putsf(0, 2, "(%d) ES9018K2M HWVOL", es9018k2m_present_flag);
+ }
+ else
+ {
+ lcd_putsf(0, 2, "(%d) SWVOL", es9018k2m_present_flag);
+ }
+#endif
lcd_update();
} while(get_action(CONTEXT_STD, HZ) != ACTION_STD_CANCEL);
return false;
}
+#ifdef X1000_CPUIDLE_STATS
static bool dbg_cpuidle(void)
{
do {
@@ -143,10 +163,19 @@ static bool dbg_cpuidle(void)
return false;
}
+#endif
#ifdef FIIO_M3K
extern bool dbg_fiiom3k_touchpad(void);
-extern bool axp173_debug_menu(void);
+#endif
+#ifdef SHANLING_Q1
+extern bool dbg_shanlingq1_touchscreen(void);
+#endif
+#ifdef HAVE_AXP_PMU
+extern bool axp_debug_menu(void);
+#endif
+#ifdef HAVE_CW2015
+extern bool cw2015_debug_menu(void);
#endif
/* Menu definition */
@@ -156,11 +185,21 @@ static const struct {
} menuitems[] = {
{"Clocks", &dbg_clocks},
{"GPIOs", &dbg_gpios},
+#ifdef X1000_CPUIDLE_STATS
{"CPU idle", &dbg_cpuidle},
+#endif
{"Audio", &dbg_audio},
#ifdef FIIO_M3K
{"Touchpad", &dbg_fiiom3k_touchpad},
- {"Power stats", &axp173_debug_menu},
+#endif
+#ifdef SHANLING_Q1
+ {"Touchscreen", &dbg_shanlingq1_touchscreen},
+#endif
+#ifdef HAVE_AXP_PMU
+ {"Power stats", &axp_debug_menu},
+#endif
+#ifdef HAVE_CW2015
+ {"CW2015 debug", &cw2015_debug_menu},
#endif
};
diff --git a/firmware/target/mips/ingenic_x1000/dma-x1000.h b/firmware/target/mips/ingenic_x1000/dma-x1000.h
index d836a0cf54..4a526cec02 100644
--- a/firmware/target/mips/ingenic_x1000/dma-x1000.h
+++ b/firmware/target/mips/ingenic_x1000/dma-x1000.h
@@ -43,8 +43,9 @@
* cannot be used safely.
*/
#define DMA_CHANNEL_AUDIO 0
-#define DMA_CHANNEL_FBCOPY 1
-#define DMA_NUM_USED_CHANNELS 2
+#define DMA_CHANNEL_RECORD 1
+#define DMA_CHANNEL_FBCOPY 2
+#define DMA_NUM_USED_CHANNELS 3
struct dma_desc {
uint32_t cm; /* meaning and layout same as DMA_CHN_CM */
@@ -63,7 +64,7 @@ typedef struct dma_desc dma_desc;
typedef void(*dma_cb_func)(int event);
-extern void dma_init(void);
+extern void dma_init(void) INIT_ATTR;
extern void dma_set_callback(int chn, dma_cb_func cb);
#endif /* __DMA_X1000_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/adc-target.h b/firmware/target/mips/ingenic_x1000/erosqnative/adc-target.h
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/erosqnative/adc-target.h
diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/audiohw-erosqnative.c b/firmware/target/mips/ingenic_x1000/erosqnative/audiohw-erosqnative.c
new file mode 100644
index 0000000000..df97aba0c8
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/erosqnative/audiohw-erosqnative.c
@@ -0,0 +1,182 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald, Dana Conrad
+ *
+ * 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 "pcm_sw_volume.h"
+#include "pcm_sampr.h"
+#include "i2c-target.h"
+#include "button.h"
+
+// #define LOGF_ENABLE
+#include "logf.h"
+
+#include "aic-x1000.h"
+#include "i2c-x1000.h"
+#include "gpio-x1000.h"
+
+/*
+ * Earlier devices audio path appears to be:
+ * DAC \--> HP Amp --> Stereo Switch --> HP OUT
+ * \-> LO OUT
+ *
+ * Recent devices, the audio path seems to have changed to:
+ * DAC --> HP Amp --> Stereo Switch \--> HP OUT
+ * \-> LO OUT
+ */
+
+void audiohw_init(void)
+{
+ /* explicitly mute everything */
+ gpio_set_level(GPIO_HPAMP_SHDN, 0);
+ gpio_set_level(GPIO_STEREOSW_MUTE, 1);
+ gpio_set_level(GPIO_DAC_PWR, 0);
+
+ aic_set_play_last_sample(true);
+ aic_set_external_codec(true);
+ aic_set_i2s_mode(AIC_I2S_MASTER_MODE);
+ audiohw_set_frequency(HW_FREQ_48);
+
+ aic_enable_i2s_master_clock(true);
+ aic_enable_i2s_bit_clock(true);
+
+ mdelay(10);
+
+ /* power on DAC and HP Amp */
+ gpio_set_level(GPIO_DAC_ANALOG_PWR, 1);
+ gpio_set_level(GPIO_HPAMP_POWER, 1);
+}
+
+void audiohw_postinit(void)
+{
+ /*
+ * enable playback, fill FIFO buffer with -1 to prevent
+ * the DAC from auto-muting, wait, and then stop playback.
+ * This seems to completely prevent power-on or first-track
+ * clicking.
+ */
+ jz_writef(AIC_CCR, ERPL(1));
+ for (int i = 0; i < 32; i++)
+ {
+ jz_write(AIC_DR, 0xFFFFFF);
+ }
+ /* Wait until all samples are through the FIFO. */
+ while(jz_readf(AIC_SR, TFL) != 0);
+ mdelay(20); /* This seems to silence the power-on click */
+ jz_writef(AIC_CCR, ERPL(0));
+
+ /* unmute - attempt to make power-on pop-free */
+ gpio_set_level(GPIO_STEREOSW_SEL, 0);
+ gpio_set_level(GPIO_HPAMP_SHDN, 1);
+ mdelay(10);
+ gpio_set_level(GPIO_DAC_PWR, 1);
+ mdelay(10);
+ gpio_set_level(GPIO_STEREOSW_MUTE, 0);
+
+ i2c_x1000_set_freq(ES9018K2M_BUS, I2C_FREQ_400K);
+
+ int ret = es9018k2m_read_reg(ES9018K2M_REG0_SYSTEM_SETTINGS);
+ if (ret >= 0) /* Detected ES9018K2M DAC */
+ {
+ logf("ES9018K2M found: ret=%d", ret);
+ es9018k2m_present_flag = 1;
+
+ /* Default is 32-bit data, and it works ok. Enabling the following
+ * causes issue. Which is weird, I definitely thought AIC was configured
+ * for 24-bit data... */
+ // es9018k2m_write_reg(ES9018K2M_REG1_INPUT_CONFIG, 0b01001100); // 24-bit data
+
+ } else { /* Default to SWVOL for PCM5102A DAC */
+ logf("Default to SWVOL: ret=%d", ret);
+ }
+}
+
+void audiohw_close(void)
+{
+ /* mute - attempt to make power-off pop-free */
+ gpio_set_level(GPIO_STEREOSW_MUTE, 1);
+ mdelay(10);
+ gpio_set_level(GPIO_DAC_PWR, 0);
+ mdelay(10);
+ gpio_set_level(GPIO_HPAMP_SHDN, 0);
+}
+
+void audiohw_set_frequency(int fsel)
+{
+ int sampr = hw_freq_sampr[fsel];
+ int mult = 256;
+
+ aic_enable_i2s_bit_clock(false);
+ aic_set_i2s_clock(X1000_CLK_SCLK_A, sampr, mult);
+ aic_enable_i2s_bit_clock(true);
+}
+
+void audiohw_set_volume(int vol_l, int vol_r)
+{
+ int l, r;
+
+ eros_qn_set_last_vol(vol_l, vol_r);
+
+ l = vol_l;
+ r = vol_r;
+
+#if (defined(HAVE_HEADPHONE_DETECTION) && defined(HAVE_LINEOUT_DETECTION))
+ /* make sure headphones aren't present - don't want to
+ * blow out our eardrums cranking it to full */
+ if (lineout_inserted() && !headphones_inserted())
+ {
+ eros_qn_switch_output(1);
+
+ l = r = eros_qn_get_volume_limit();
+ }
+ else
+ {
+ eros_qn_switch_output(0);
+ }
+#endif
+
+ if (es9018k2m_present_flag) /* ES9018K2M */
+ {
+ /* Same volume range and mute point for both DACs, so use PCM5102A_VOLUME_MIN */
+ l = l <= PCM5102A_VOLUME_MIN ? PCM_MUTE_LEVEL : l;
+ r = r <= PCM5102A_VOLUME_MIN ? PCM_MUTE_LEVEL : r;
+
+ /* set software volume just below unity due to
+ * DAC offset. We don't want to overflow the PCM system. */
+ pcm_set_master_volume(-1, -1);
+ es9018k2m_set_volume_async(l, r);
+ }
+ else /* PCM5102A */
+ {
+ l = l <= PCM5102A_VOLUME_MIN ? PCM_MUTE_LEVEL : (l / 20);
+ r = r <= PCM5102A_VOLUME_MIN ? PCM_MUTE_LEVEL : (r / 20);
+
+ pcm_set_master_volume(l, r);
+ }
+}
+
+void audiohw_set_filter_roll_off(int value)
+{
+ if (es9018k2m_present_flag)
+ {
+ es9018k2m_set_filter_roll_off(value);
+ }
+} \ No newline at end of file
diff --git a/firmware/target/arm/tatung/tpj1022/backlight-tpj1022.c b/firmware/target/mips/ingenic_x1000/erosqnative/backlight-erosqnative.c
index d63387728b..2c86a995db 100644
--- a/firmware/target/arm/tatung/tpj1022/backlight-tpj1022.c
+++ b/firmware/target/mips/ingenic_x1000/erosqnative/backlight-erosqnative.c
@@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Copyright (C) 2006 by Barry Wardell
+ * Copyright (C) 2021 Aidan MacDonald, Dana Conrad
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -19,28 +19,45 @@
*
****************************************************************************/
-/* The H10 display (and hence backlight) possibly identical to that of the X5,
- so that code was used here but left #if 0'ed out for the moment */
-
-#include "config.h"
-#include "cpu.h"
-#include "system.h"
#include "backlight.h"
+#include "backlight-target.h"
+#include "lcd.h"
+#include "pwm-x1000.h"
+
+#define BL_LCD_CHN 0
+#define BL_LCD_PERIOD 30000
+
+static int backlight_calc_duty(int period, int min_duty, int brightness)
+{
+ return min_duty + (period - min_duty) * brightness / MAX_BRIGHTNESS_SETTING;
+}
+
+bool backlight_hw_init(void)
+{
+ pwm_init(BL_LCD_CHN);
+ pwm_enable(BL_LCD_CHN);
+ backlight_hw_brightness(MAX_BRIGHTNESS_SETTING);
+ return true;
+}
void backlight_hw_on(void)
{
-#if 0
- int level = disable_irq_save();
- pcf50606_write(0x38, 0xb0); /* Backlight ON, GPO1INV=1, GPO1ACT=011 */
- restore_irq(level);
+ pwm_enable(BL_LCD_CHN);
+#ifdef HAVE_LCD_ENABLE
+ lcd_enable(true);
#endif
}
void backlight_hw_off(void)
{
-#if 0
- int level = disable_irq_save();
- pcf50606_write(0x38, 0x80); /* Backlight OFF, GPO1INV=1, GPO1ACT=000 */
- restore_irq(level);
+ pwm_disable(BL_LCD_CHN);
+#ifdef HAVE_LCD_ENABLE
+ lcd_enable(false);
#endif
}
+
+void backlight_hw_brightness(int brightness)
+{
+ int duty_ns = backlight_calc_duty(BL_LCD_PERIOD, 0, brightness);
+ pwm_set_period(BL_LCD_CHN, BL_LCD_PERIOD, duty_ns);
+}
diff --git a/firmware/target/arm/tcc77x/logikdax/power-logikdax.c b/firmware/target/mips/ingenic_x1000/erosqnative/backlight-target.h
index 840b02a2a5..05d6e36679 100644
--- a/firmware/target/arm/tcc77x/logikdax/power-logikdax.c
+++ b/firmware/target/mips/ingenic_x1000/erosqnative/backlight-target.h
@@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Copyright (C) 2007 Dave Chapman
+ * Copyright (C) 2021 Aidan MacDonald, Dana Conrad
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -18,27 +18,16 @@
* KIND, either express or implied.
*
****************************************************************************/
-#include "config.h"
-#include "cpu.h"
-#include <stdbool.h>
-#include "kernel.h"
-#include "system.h"
-#include "power.h"
-void power_init(void)
-{
-}
+#ifndef __BACKLIGHT_TARGET_H__
+#define __BACKLIGHT_TARGET_H__
+
+#include <stdbool.h>
-void ide_power_enable(bool on)
-{
- (void)on;
-}
+extern bool backlight_hw_init(void);
-bool ide_powered(void)
-{
- return true;
-}
+extern void backlight_hw_on(void);
+extern void backlight_hw_off(void);
+extern void backlight_hw_brightness(int brightness);
-void power_off(void)
-{
-}
+#endif /* __BACKLIGHT_TARGET_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/button-erosqnative.c b/firmware/target/mips/ingenic_x1000/erosqnative/button-erosqnative.c
new file mode 100644
index 0000000000..0d2207af2a
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/erosqnative/button-erosqnative.c
@@ -0,0 +1,251 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald, Dana Conrad
+ *
+ * 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 "button.h"
+#include "kernel.h"
+#include "backlight.h"
+#include "powermgmt.h"
+#include "panic.h"
+#include "axp-pmu.h"
+#include "gpio-x1000.h"
+#include "irq-x1000.h"
+#include "i2c-x1000.h"
+#include "eros_qn_codec.h"
+#include <string.h>
+#include <stdbool.h>
+
+#ifndef BOOTLOADER
+# include "lcd.h"
+# include "font.h"
+#endif
+
+/* ===========================================
+ * | OLD STATE | NEW STATE | DIRECTION |
+ * | 0 0 | 0 0 | 0: NO CHANGE |
+ * | 0 0 | 0 1 | -1: CCW |
+ * | 0 0 | 1 0 | 1: CW |
+ * | 0 0 | 1 1 | 0: INVALID |
+ * | 0 1 | 0 0 | 1: CW |
+ * | 0 1 | 0 1 | 0: NO CHANGE |
+ * | 0 1 | 1 0 | 0: INVALID |
+ * | 0 1 | 1 1 | -1: CCW |
+ * | 1 0 | 0 0 | -1: CCW |
+ * | 1 0 | 0 1 | 0: INVALID |
+ * | 1 0 | 1 0 | 0: NO CHANGE |
+ * | 1 0 | 1 1 | 1: CW |
+ * | 1 1 | 0 0 | 0: INVALID |
+ * | 1 1 | 0 1 | 1: CW |
+ * | 1 1 | 1 0 | -1: CCW |
+ * | 1 1 | 1 1 | 0: NO CHANGE |
+ * ===========================================
+ *
+ * Quadrature explanation since it's not plainly obvious how this works:
+ *
+ * If either of the quadrature lines change, we can look up the combination
+ * of previous state and new state in the table above (enc_state[] below)
+ * and it tells us whether to add 1, subtract 1, or no change from the sum (enc_position).
+ * This also gives us a nice debounce, since each state can only have 1 pin change
+ * at a time. I didn't come up with this, but I've used it before and it works well.
+ *
+ * Old state is 2 higher bits, new state is 2 lower bits of enc_current_state. */
+
+/* list of valid quadrature states and their directions */
+signed char enc_state[] = {0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0};
+volatile unsigned char enc_current_state = 0;
+volatile signed int enc_position = 0;
+
+/* Value of headphone detect register */
+static uint8_t hp_detect_reg = 0x00;
+static uint8_t hp_detect_reg_old = 0x00;
+
+/* Interval to poll the register */
+#define HPD_POLL_TIME (HZ/2)
+
+static int hp_detect_tmo_cb(struct timeout* tmo)
+{
+ i2c_descriptor* d = (i2c_descriptor*)tmo->data;
+ i2c_async_queue(AXP_PMU_BUS, TIMEOUT_NOBLOCK, I2C_Q_ADD, 0, d);
+ return HPD_POLL_TIME;
+}
+
+static void hp_detect_init(void)
+{
+ static struct timeout tmo;
+ static const uint8_t gpio_reg = AXP192_REG_GPIOSTATE1;
+ static i2c_descriptor desc = {
+ .slave_addr = AXP_PMU_ADDR,
+ .bus_cond = I2C_START | I2C_STOP,
+ .tran_mode = I2C_READ,
+ .buffer[0] = (void*)&gpio_reg,
+ .count[0] = 1,
+ .buffer[1] = &hp_detect_reg,
+ .count[1] = 1,
+ .callback = NULL,
+ .arg = 0,
+ .next = NULL,
+ };
+
+ /* Headphone and LO detects are wired to AXP192 GPIOs 0 and 1,
+ * set them to inputs. */
+ i2c_reg_write1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP192_REG_GPIO0FUNCTION, 0x01); /* HP detect */
+ i2c_reg_write1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP192_REG_GPIO1FUNCTION, 0x01); /* LO detect */
+
+ /* Get an initial reading before startup */
+ int r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, gpio_reg);
+ if(r >= 0)
+ {
+ hp_detect_reg = r;
+ hp_detect_reg_old = hp_detect_reg;
+ }
+
+ /* Poll the register every second */
+ timeout_register(&tmo, &hp_detect_tmo_cb, HPD_POLL_TIME, (intptr_t)&desc);
+}
+
+bool headphones_inserted(void)
+{
+ /* if the status has changed, set the output volume accordingly */
+ if ((hp_detect_reg & 0x30) != (hp_detect_reg_old & 0x30))
+ {
+ hp_detect_reg_old = hp_detect_reg;
+#if !defined(BOOTLOADER)
+ eros_qn_set_outputs();
+#endif
+ }
+ return hp_detect_reg & 0x10 ? false : true;
+}
+
+bool lineout_inserted(void)
+{
+ /* if the status has changed, set the output volume accordingly */
+ if ((hp_detect_reg & 0x30) != (hp_detect_reg_old & 0x30))
+ {
+ hp_detect_reg_old = hp_detect_reg;
+#if !defined(BOOTLOADER)
+ eros_qn_set_outputs();
+#endif
+ }
+ return hp_detect_reg & 0x20 ? false : true;
+}
+
+/* Rockbox interface */
+void button_init_device(void)
+{
+ /* set both quadrature lines to interrupts */
+ gpio_set_function(GPIO_BTN_SCROLL_A, GPIOF_IRQ_EDGE(1));
+ gpio_set_function(GPIO_BTN_SCROLL_B, GPIOF_IRQ_EDGE(1));
+
+ /* set interrupts to fire on the next edge based on current state */
+ gpio_flip_edge_irq(GPIO_BTN_SCROLL_A);
+ gpio_flip_edge_irq(GPIO_BTN_SCROLL_B);
+
+ /* get current state of both encoder gpios */
+ enc_current_state = (REG_GPIO_PIN(GPIO_B)>>21) & 0x0c;
+
+ /* enable quadrature interrupts */
+ gpio_enable_irq(GPIO_BTN_SCROLL_A);
+ gpio_enable_irq(GPIO_BTN_SCROLL_B);
+
+ /* Set up headphone and line out detect polling */
+ hp_detect_init();
+}
+
+/* wheel Quadrature line A interrupt */
+void GPIOB24(void)
+{
+ /* fill state with previous (2 higher bits) and current (2 lower bits) */
+ enc_current_state = (enc_current_state & 0x0c) | ((REG_GPIO_PIN(GPIO_B)>>23) & 0x03);
+
+ /* look up in table */
+ enc_position = enc_position + enc_state[(enc_current_state)];
+
+ /* move current state to previous state if valid data */
+ if (enc_state[(enc_current_state)] != 0)
+ enc_current_state = (enc_current_state << 2);
+
+ /* we want the other edge next time */
+ gpio_flip_edge_irq(GPIO_BTN_SCROLL_A);
+}
+
+/* wheel Quadrature line B interrupt */
+void GPIOB23(void)
+{
+ /* fill state with previous (2 higher bits) and current (2 lower bits) */
+ enc_current_state = (enc_current_state & 0x0c) | ((REG_GPIO_PIN(GPIO_B)>>23) & 0x03);
+
+ /* look up in table */
+ enc_position = enc_position + enc_state[(enc_current_state)];
+
+ /* move current state to previous state if valid data */
+ if (enc_state[(enc_current_state)] != 0)
+ enc_current_state = (enc_current_state << 2);
+
+ /* we want the other edge next time */
+ gpio_flip_edge_irq(GPIO_BTN_SCROLL_B);
+}
+
+int button_read_device(void)
+{
+ int r = 0;
+
+ /* Read GPIOs for normal buttons */
+ uint32_t a = REG_GPIO_PIN(GPIO_A);
+ uint32_t b = REG_GPIO_PIN(GPIO_B);
+ uint32_t c = REG_GPIO_PIN(GPIO_C);
+ uint32_t d = REG_GPIO_PIN(GPIO_D);
+
+ /* All buttons are active low */
+ if((a & (1 << 16)) == 0) r |= BUTTON_PLAY;
+ if((a & (1 << 17)) == 0) r |= BUTTON_VOL_UP;
+ if((a & (1 << 19)) == 0) r |= BUTTON_VOL_DOWN;
+
+ if((b & (1 << 7)) == 0) r |= BUTTON_POWER;
+ if((b & (1 << 28)) == 0) r |= BUTTON_MENU;
+ if((b & (1 << 28)) == 0) r |= BUTTON_MENU;
+
+ if((d & (1 << 4)) == 0) r |= BUTTON_PREV;
+
+ if((d & (1 << 5)) == 0) r |= BUTTON_BACK;
+ if((c & (1 << 24)) == 0) r |= BUTTON_NEXT;
+
+ /* check encoder - from testing, each indent is 2 state changes or so */
+ if (enc_position > 1)
+ {
+ /* need to use queue_post() in order to do BUTTON_SCROLL_*,
+ * Rockbox treats these buttons differently. */
+ queue_post(&button_queue, BUTTON_SCROLL_FWD, 0);
+ enc_position = 0;
+ reset_poweroff_timer();
+ backlight_on();
+ }
+ else if (enc_position < -1)
+ {
+ /* need to use queue_post() in order to do BUTTON_SCROLL_*,
+ * Rockbox treats these buttons differently. */
+ queue_post(&button_queue, BUTTON_SCROLL_BACK, 0);
+ enc_position = 0;
+ reset_poweroff_timer();
+ backlight_on();
+ }
+
+ return r;
+}
+
diff --git a/firmware/target/arm/tcc77x/c100/button-target.h b/firmware/target/mips/ingenic_x1000/erosqnative/button-target.h
index 0e8b7e8f1c..9c39a40296 100644
--- a/firmware/target/arm/tcc77x/c100/button-target.h
+++ b/firmware/target/mips/ingenic_x1000/erosqnative/button-target.h
@@ -5,9 +5,8 @@
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
- * $Id$
*
- * Copyright (C) 2007 by Dave Chapman
+ * Copyright (C) 2021 Solomon Peachy, Dana Conrad
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -18,28 +17,29 @@
* KIND, either express or implied.
*
****************************************************************************/
-
#ifndef _BUTTON_TARGET_H_
#define _BUTTON_TARGET_H_
-#define HAS_BUTTON_HOLD
-
/* Main unit's buttons */
-#define BUTTON_POWER 0x00000001
-#define BUTTON_VOLUP 0x00000002
-#define BUTTON_VOLDOWN 0x00000004
-#define BUTTON_UP 0x00000008
-#define BUTTON_DOWN 0x00000010
-#define BUTTON_LEFT 0x00000020
-#define BUTTON_RIGHT 0x00000040
-#define BUTTON_SELECT 0x00000080
+#define BUTTON_POWER 0x00000001
+#define BUTTON_MENU 0x00000002
+#define BUTTON_BACK 0x00000004
+#define BUTTON_PLAY 0x00000008
+#define BUTTON_NEXT 0x00000010
+#define BUTTON_PREV 0x00000020
+#define BUTTON_VOL_UP 0x00000040
+#define BUTTON_VOL_DOWN 0x00000080
+#define BUTTON_SCROLL_BACK 0x00000100
+#define BUTTON_SCROLL_FWD 0x00000200
-#define BUTTON_MAIN (BUTTON_POWER|BUTTON_VOLUP|BUTTON_VOLDOWN\
- |BUTTON_UP|BUTTON_DOWN|BUTTON_LEFT\
- |BUTTON_RIGHT|BUTTON_SELECT)
+#define BUTTON_MAIN (BUTTON_POWER | BUTTON_MENU | BUTTON_BACK | BUTTON_PREV | \
+ BUTTON_NEXT | BUTTON_PLAY | BUTTON_VOL_UP | BUTTON_VOL_DOWN | BUTTON_SCROLL_BACK | BUTTON_SCROLL_FWD)
+
+#define BUTTON_LEFT BUTTON_PREV
+#define BUTTON_RIGHT BUTTON_NEXT
/* Software power-off */
#define POWEROFF_BUTTON BUTTON_POWER
-#define POWEROFF_COUNT 40
-
+#define POWEROFF_COUNT 25
+
#endif /* _BUTTON_TARGET_H_ */
diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/gpio-target.h b/firmware/target/mips/ingenic_x1000/erosqnative/gpio-target.h
new file mode 100644
index 0000000000..72052c261f
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/erosqnative/gpio-target.h
@@ -0,0 +1,75 @@
+/* -------------------- NOTES ------------------- */
+
+/* I don't think we have any devices on I2C1, the pins /may/ be reused. */
+/* DEFINE_PINGROUP(I2C1, GPIO_C, 3 << 26, GPIOF_DEVICE(0)) */
+
+/* OF has SD Card power listed as 0x2a - PB10, but it seems to work without. */
+
+/* I think BT power reg is pin 0x53 - C19 */
+
+/* USB_DETECT D3 chosen by trial-and-error. */
+
+/* I have a suspicion this isn't right for AXP_IRQ,
+ * and it's not used right now anyway. copied from m3k. */
+/* DEFINE_GPIO(AXP_IRQ, GPIO_PB(10), GPIOF_INPUT) */
+
+/* ---------------------------------------------- */
+
+
+/* Name Port Pins Function */
+DEFINE_PINGROUP(LCD_DATA, GPIO_A, 0xffff << 0, GPIOF_DEVICE(1))
+DEFINE_PINGROUP(LCD_CONTROL, GPIO_B, 0x1a << 16, GPIOF_DEVICE(1))
+DEFINE_PINGROUP(MSC0, GPIO_A, 0x3f << 20, GPIOF_DEVICE(1))
+DEFINE_PINGROUP(SFC, GPIO_A, 0x3f << 26, GPIOF_DEVICE(1))
+DEFINE_PINGROUP(I2S, GPIO_B, 0x1f << 0, GPIOF_DEVICE(1))
+DEFINE_PINGROUP(I2C1, GPIO_C, 3 << 26, GPIOF_DEVICE(0))
+DEFINE_PINGROUP(I2C2, GPIO_D, 3 << 0, GPIOF_DEVICE(1))
+
+/* Name Pin Function */
+/* mute DAC: 0 - mute, 1 - play */
+/* Note: This seems to actually be power to the DAC in general,
+ * at least on the ES9018K2M devices. Was "DAC_XMIT". */
+DEFINE_GPIO(DAC_PWR, GPIO_PB(12), GPIOF_OUTPUT(0))
+
+/* mute HP amp: 0 - mute, 1 - play */
+DEFINE_GPIO(HPAMP_SHDN, GPIO_PB(8), GPIOF_OUTPUT(0))
+
+/* mute audio mux: 0 - play, 1 - mute */
+DEFINE_GPIO(STEREOSW_MUTE, GPIO_PB(15), GPIOF_OUTPUT(1))
+
+/*
+ * Original devices: switches HP on/off - 0 HP on, 1 HP off, no effect on LO.
+ * Newer devices: switches between HP and LO - 0 HP, 1 LO.
+ */
+DEFINE_GPIO(STEREOSW_SEL, GPIO_PB(5), GPIOF_OUTPUT(0))
+
+/* DAC AVDD */
+DEFINE_GPIO(DAC_ANALOG_PWR, GPIO_PB(9), GPIOF_OUTPUT(0))
+
+/* Headphone Amp power */
+DEFINE_GPIO(HPAMP_POWER, GPIO_PB(6), GPIOF_OUTPUT(0))
+
+/* SD card */
+DEFINE_GPIO(MSC0_CD, GPIO_PB(11), GPIOF_INPUT)
+
+/* USB */
+DEFINE_GPIO(USB_DETECT, GPIO_PD(3), GPIOF_INPUT)
+DEFINE_GPIO(USB_DRVVBUS, GPIO_PB(25), GPIOF_OUTPUT(0))
+
+/* LCD */
+DEFINE_GPIO(LCD_PWR, GPIO_PB(14), GPIOF_OUTPUT(0))
+DEFINE_GPIO(LCD_RESET, GPIO_PB(13), GPIOF_OUTPUT(0))
+DEFINE_GPIO(LCD_CE, GPIO_PB(18), GPIOF_OUTPUT(1))
+DEFINE_GPIO(LCD_RD, GPIO_PB(16), GPIOF_OUTPUT(1))
+
+/* Buttons */
+DEFINE_GPIO(BTN_PLAY, GPIO_PA(16), GPIOF_INPUT)
+DEFINE_GPIO(BTN_VOL_UP, GPIO_PA(17), GPIOF_INPUT)
+DEFINE_GPIO(BTN_VOL_DOWN, GPIO_PA(19), GPIOF_INPUT)
+DEFINE_GPIO(BTN_POWER, GPIO_PB(7), GPIOF_INPUT)
+DEFINE_GPIO(BTN_MENU, GPIO_PB(28), GPIOF_INPUT)
+DEFINE_GPIO(BTN_BACK, GPIO_PD(5), GPIOF_INPUT)
+DEFINE_GPIO(BTN_PREV, GPIO_PD(4), GPIOF_INPUT)
+DEFINE_GPIO(BTN_NEXT, GPIO_PC(24), GPIOF_INPUT)
+DEFINE_GPIO(BTN_SCROLL_A, GPIO_PB(24), GPIOF_INPUT)
+DEFINE_GPIO(BTN_SCROLL_B, GPIO_PB(23), GPIOF_INPUT)
diff --git a/firmware/target/arm/tatung/tpj1022/backlight-target.h b/firmware/target/mips/ingenic_x1000/erosqnative/i2c-target.h
index 4a5e677599..89d995f33a 100644
--- a/firmware/target/arm/tatung/tpj1022/backlight-target.h
+++ b/firmware/target/mips/ingenic_x1000/erosqnative/i2c-target.h
@@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Copyright (C) 2006 by Barry Wardell
+ * Copyright (C) 2021 Aidan MacDonald, Dana Conrad
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -19,13 +19,16 @@
*
****************************************************************************/
-/* Taken from the x5's implementation */
+#ifndef __I2C_TARGET_H__
+#define __I2C_TARGET_H__
-#ifndef BACKLIGHT_TARGET_H
-#define BACKLIGHT_TARGET_H
+#define I2C_ASYNC_BUS_COUNT 3
+#define I2C_ASYNC_QUEUE_SIZE 4
-#define backlight_hw_init() true
-void backlight_hw_on(void);
-void backlight_hw_off(void);
+#define ES9018K2M_BUS 1
+#define ES9018K2M_ADDR 0x48
-#endif
+#define AXP_PMU_BUS 2
+#define AXP_PMU_ADDR 0x34
+
+#endif /* __I2C_TARGET_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/lcd-erosqnative.c b/firmware/target/mips/ingenic_x1000/erosqnative/lcd-erosqnative.c
new file mode 100644
index 0000000000..0d43a3f010
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/erosqnative/lcd-erosqnative.c
@@ -0,0 +1,211 @@
+
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald, Dana Conrad
+ *
+ * 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 "lcd.h"
+#include "kernel.h"
+#include "lcd-x1000.h"
+#include "gpio-x1000.h"
+#include "system.h"
+
+/* for reference on these command/data hex values, see the mipi dcs lcd spec. *
+ * Not everything here is there, but all the standard stuff is. */
+
+static const uint32_t erosqnative_lcd_cmd_enable[] = {
+ /* Set EXTC? */
+ LCD_INSTR_CMD, 0xc8,
+ LCD_INSTR_DAT, 0xff,
+ LCD_INSTR_DAT, 0x93,
+ LCD_INSTR_DAT, 0x42,
+ /* Set Address Mode */
+ LCD_INSTR_CMD, 0x36,
+ LCD_INSTR_DAT, 0xd8,
+ /* Pixel Format Set */
+ LCD_INSTR_CMD, 0x3a,
+ //LCD_INSTR_DAT, 0x66, /* OF specified 18 bpp */
+ LCD_INSTR_DAT, 0x05, /* RB seems to be happier dealing with 16 bits/pixel */
+ /* Power Control 1? */
+ LCD_INSTR_CMD, 0xc0,
+ LCD_INSTR_DAT, 0x15,
+ LCD_INSTR_DAT, 0x15,
+ /* Power Control 2? */
+ LCD_INSTR_CMD, 0xc1,
+ LCD_INSTR_DAT, 0x01,
+ /* VCOM? */
+ LCD_INSTR_CMD, 0xc5,
+ LCD_INSTR_DAT, 0xda,
+ /* ?? */
+ LCD_INSTR_CMD, 0xb1,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x1b,
+ /* ?? */
+ LCD_INSTR_CMD, 0xb4,
+ LCD_INSTR_DAT, 0x02,
+ /* Positive gamma correction? */
+ LCD_INSTR_CMD, 0xe0,
+ LCD_INSTR_DAT, 0x0f,
+ LCD_INSTR_DAT, 0x13,
+ LCD_INSTR_DAT, 0x17,
+ LCD_INSTR_DAT, 0x04,
+ LCD_INSTR_DAT, 0x13,
+ LCD_INSTR_DAT, 0x07,
+ LCD_INSTR_DAT, 0x40,
+ LCD_INSTR_DAT, 0x39,
+ LCD_INSTR_DAT, 0x4f,
+ LCD_INSTR_DAT, 0x06,
+ LCD_INSTR_DAT, 0x0d,
+ LCD_INSTR_DAT, 0x0a,
+ LCD_INSTR_DAT, 0x1f,
+ LCD_INSTR_DAT, 0x22,
+ LCD_INSTR_DAT, 0x00,
+ /* Negative gamma correction? */
+ LCD_INSTR_CMD, 0xe1,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x21,
+ LCD_INSTR_DAT, 0x24,
+ LCD_INSTR_DAT, 0x03,
+ LCD_INSTR_DAT, 0x0f,
+ LCD_INSTR_DAT, 0x05,
+ LCD_INSTR_DAT, 0x38,
+ LCD_INSTR_DAT, 0x32,
+ LCD_INSTR_DAT, 0x49,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x09,
+ LCD_INSTR_DAT, 0x08,
+ LCD_INSTR_DAT, 0x32,
+ LCD_INSTR_DAT, 0x35,
+ LCD_INSTR_DAT, 0x0f,
+ /* Exit Sleep */
+ LCD_INSTR_CMD, 0x11,
+ LCD_INSTR_UDELAY, 120000,
+ /* Display On */
+ LCD_INSTR_CMD, 0x29,
+ LCD_INSTR_UDELAY, 20000,
+ LCD_INSTR_END,
+};
+
+static const uint32_t erosqnative_lcd_of_compat_cmd[] = {
+ /* Pixel Format Set */
+ LCD_INSTR_CMD, 0x3a,
+ LCD_INSTR_DAT, 0x66, /* 18 bpp */
+ /* Exit Sleep */
+ LCD_INSTR_CMD, 0x11,
+ LCD_INSTR_UDELAY, 120000,
+ /* Display On */
+ LCD_INSTR_CMD, 0x29,
+ LCD_INSTR_UDELAY, 20000,
+ LCD_INSTR_END,
+};
+
+/* sleep and wake copied directly from m3k */
+static const uint32_t erosqnative_lcd_cmd_sleep[] = {
+ /* Display OFF */
+ LCD_INSTR_CMD, 0x28,
+ /* Sleep IN */
+ LCD_INSTR_CMD, 0x10,
+ LCD_INSTR_UDELAY, 5000,
+ LCD_INSTR_END,
+};
+
+static const uint32_t erosqnative_lcd_cmd_wake[] = {
+ /* Sleep OUT */
+ LCD_INSTR_CMD, 0x11,
+ LCD_INSTR_UDELAY, 5000,
+ /* Display ON */
+ LCD_INSTR_CMD, 0x29,
+ LCD_INSTR_END,
+};
+
+/* As far as I can tell, this is a sequence of commands sent before each
+ * DMA set. Original in OF was:
+ * {0x2c, 0x2c, 0x2c, 0x2c}
+ * But this set from the m3k seems to work the same, and makes more sense
+ * to me:
+ * {0x00, 0x00, 0x00, 0x2c}
+ * This command is more than likely going to be the same
+ * for any old mipi lcd on the market, maybe. I really don't think we need
+ * to send "write_memory_start four times in a row. */
+static const uint8_t __attribute__((aligned(64)))
+ erosqnative_lcd_dma_wr_cmd[] = {0x2c, 0x2c, 0x2c, 0x2c};
+
+const struct lcd_tgt_config lcd_tgt_config = {
+ .bus_width = 8,
+ .cmd_width = 8,
+ .use_6800_mode = 0,
+ .use_serial = 0,
+ .clk_polarity = 0,
+ .dc_polarity = 0,
+ .wr_polarity = 1,
+ .te_enable = 0, /* OF had TE enabled (1) */
+ .te_polarity = 1,
+ .te_narrow = 0,
+ .dma_wr_cmd_buf = &erosqnative_lcd_dma_wr_cmd,
+ .dma_wr_cmd_size = sizeof(erosqnative_lcd_dma_wr_cmd),
+};
+
+void lcd_tgt_enable(bool enable)
+{
+ if(enable) {
+ /* power up the panel */
+ gpio_set_level(GPIO_LCD_PWR, 1);
+ mdelay(20);
+ gpio_set_level(GPIO_LCD_RESET, 1);
+ mdelay(12);
+
+ /* set the clock */
+ lcd_set_clock(X1000_CLK_SCLK_A, 20000000);
+
+ /* toggle chip select low (active) */
+ gpio_set_level(GPIO_LCD_RD, 1);
+ gpio_set_level(GPIO_LCD_CE, 1);
+ mdelay(5);
+ gpio_set_level(GPIO_LCD_CE, 0);
+
+ lcd_exec_commands(&erosqnative_lcd_cmd_enable[0]);
+ } else {
+ /* doesn't flash white if we don't do anything... */
+#if 0
+ lcd_exec_commands(&erosqnative_lcd_cmd_sleep[0]);
+
+ mdelay(115); // copied from m3k
+
+ gpio_set_level(GPIO_LCD_PWR, 0);
+ gpio_set_level(GPIO_LCD_RESET, 0);
+#endif
+ }
+}
+
+void lcd_tgt_enable_of(bool enable)
+{
+ /* silence the unused parameter warning */
+ if (enable)
+ {}
+
+ lcd_exec_commands(&erosqnative_lcd_of_compat_cmd[0]);
+}
+
+void lcd_tgt_sleep(bool sleep)
+{
+ if(sleep)
+ lcd_exec_commands(&erosqnative_lcd_cmd_sleep[0]);
+ else
+ lcd_exec_commands(&erosqnative_lcd_cmd_wake[0]);
+}
diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/power-erosqnative.c b/firmware/target/mips/ingenic_x1000/erosqnative/power-erosqnative.c
new file mode 100644
index 0000000000..ab6393a9fe
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/erosqnative/power-erosqnative.c
@@ -0,0 +1,130 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald, Dana Conrad
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+// whole file copied from m3k
+
+#include "power.h"
+#include "adc.h"
+#include "system.h"
+#include "kernel.h"
+#ifdef HAVE_USB_CHARGING_ENABLE
+# include "usb_core.h"
+#endif
+#include "axp-pmu.h"
+#include "i2c-x1000.h"
+
+const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] =
+{
+ 3470
+};
+
+/* The OF shuts down at this voltage */
+const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] =
+{
+ 3400
+};
+
+/* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */
+const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11] =
+{
+ { 3400, 3477, 3540, 3578, 3617, 3674, 3771, 3856, 3936, 4016, 4117 }
+};
+
+/* voltages (millivolt) of 0%, 10%, ... 100% when charging enabled */
+const unsigned short percent_to_volt_charge[11] =
+{
+ 3400, 3477, 3540, 3578, 3617, 3674, 3771, 3856, 3936, 4016, 4117
+};
+
+void power_init(void)
+{
+ /* Initialize driver */
+ i2c_x1000_set_freq(2, I2C_FREQ_400K);
+ axp_init();
+
+ /* Set lowest sample rate */
+ axp_adc_set_rate(AXP_ADC_RATE_25HZ);
+
+ /* Enable required ADCs */
+ axp_adc_set_enabled(
+ (1 << ADC_BATTERY_VOLTAGE) |
+ (1 << ADC_CHARGE_CURRENT) |
+ (1 << ADC_DISCHARGE_CURRENT) |
+ (1 << ADC_VBUS_VOLTAGE) |
+ (1 << ADC_VBUS_CURRENT) |
+ (1 << ADC_INTERNAL_TEMP) |
+ (1 << ADC_APS_VOLTAGE));
+
+ /* Turn on all power outputs */
+ i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
+ AXP_REG_PWROUTPUTCTRL2, 0, 0x5f, NULL);
+ i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
+ AXP_REG_DCDCWORKINGMODE, 0, 0xc0, NULL);
+
+ /* Set the default charging current. This is the same as the
+ * OF's setting, although it's not strictly within the USB spec. */
+ axp_set_charge_current(780);
+
+#ifdef BOOTLOADER
+ /* Delay to give power outputs time to stabilize.
+ * With the power thread delay, this can apparently go as low as 50,
+ * Keeping a higher value here just to ensure the bootloader works
+ * correctly. */
+ mdelay(200);
+#endif
+}
+
+#ifdef HAVE_USB_CHARGING_ENABLE
+void usb_charging_maxcurrent_change(int maxcurrent)
+{
+ axp_set_charge_current(maxcurrent);
+}
+#endif
+
+void adc_init(void)
+{
+}
+
+void power_off(void)
+{
+ axp_power_off();
+ while(1);
+}
+
+bool charging_state(void)
+{
+ return axp_battery_status() == AXP_BATT_CHARGING;
+}
+
+int _battery_voltage(void)
+{
+ return axp_adc_read(ADC_BATTERY_VOLTAGE);
+}
+
+#if CONFIG_BATTERY_MEASURE & CURRENT_MEASURE
+int _battery_current(void)
+{
+ if(charging_state())
+ return axp_adc_read(ADC_CHARGE_CURRENT);
+ else
+ return axp_adc_read(ADC_DISCHARGE_CURRENT);
+}
+#endif
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/audiohw-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/audiohw-fiiom3k.c
index 2f43809523..9374d23a81 100644
--- a/firmware/target/mips/ingenic_x1000/fiiom3k/audiohw-fiiom3k.c
+++ b/firmware/target/mips/ingenic_x1000/fiiom3k/audiohw-fiiom3k.c
@@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Copyright (C) 2021 Aidan MacDonald
+ * Copyright (C) 2021-2022 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -20,32 +20,44 @@
****************************************************************************/
#include "audiohw.h"
+#include "audio.h"
#include "system.h"
#include "pcm_sampr.h"
-#include "logf.h"
#include "aic-x1000.h"
#include "i2c-x1000.h"
#include "gpio-x1000.h"
-#include "x1000/aic.h"
-#include "x1000/cpm.h"
+#include "logf.h"
-void audiohw_init(void)
+static int cur_audio_source = AUDIO_SRC_PLAYBACK;
+static int cur_vol_r = AK4376_MIN_VOLUME;
+static int cur_vol_l = AK4376_MIN_VOLUME;
+static int cur_recvol = 0;
+static int cur_filter_roll_off = 0;
+static int cur_fsel = HW_FREQ_48;
+static int cur_power_mode = SOUND_HIGH_POWER;
+
+static void set_ak_freqmode(void)
{
- /* Configure AIC for I2S operation */
- jz_writef(CPM_CLKGR, AIC(0));
- gpio_config(GPIO_B, 0x1f, GPIO_DEVICE(1));
- jz_writef(AIC_I2SCR, STPBK(1));
+ int freq = hw_freq_sampr[cur_fsel];
+ int mult = freq >= SAMPR_176 ? 128 : 256;
- /* Operate as I2S master, use external codec */
- jz_writef(AIC_CFG, AUSEL(1), ICDC(0), BCKD(1), SYNCD(1), LSMP(1));
- jz_writef(AIC_I2SCR, ESCLK(1), AMSL(0));
+ aic_enable_i2s_bit_clock(false);
+ aic_set_i2s_clock(X1000_CLK_SCLK_A, freq, mult);
+ ak4376_set_freqmode(cur_fsel, mult, cur_power_mode);
+ aic_enable_i2s_bit_clock(true);
+}
- /* Stereo audio, packed 16 bit samples */
- jz_writef(AIC_CCR, PACK16(1), CHANNEL(1), OSS(1));
+void audiohw_init(void)
+{
+ /* Configure AIC */
+ aic_set_external_codec(true);
+ aic_set_i2s_mode(AIC_I2S_MASTER_MODE);
+ aic_enable_i2s_master_clock(true);
/* Initialize DAC */
i2c_x1000_set_freq(AK4376_BUS, I2C_FREQ_400K);
- ak4376_init();
+ ak4376_open();
+ set_ak_freqmode();
}
void audiohw_postinit(void)
@@ -54,28 +66,141 @@ void audiohw_postinit(void)
void audiohw_close(void)
{
- ak4376_close();
+ if(cur_audio_source == AUDIO_SRC_PLAYBACK)
+ ak4376_close();
+ else if(cur_audio_source == AUDIO_SRC_MIC)
+ x1000_icodec_close();
}
-void ak4376_set_pdn_pin(int level)
+void audio_set_output_source(int source)
{
- gpio_config(GPIO_A, 1 << 16, GPIO_OUTPUT(level ? 1 : 0));
+ /* this is a no-op */
+ (void)source;
}
-int ak4376_set_mclk_freq(int hw_freq, bool enabled)
+void audio_input_mux(int source, unsigned flags)
{
- /* Get the multiplier */
- int freq = hw_freq_sampr[hw_freq];
- int mult = freq >= SAMPR_176 ? 128 : 256;
+ (void)flags;
+
+ if(source == cur_audio_source)
+ return;
+
+ /* close whatever codec is currently in use */
+ audiohw_close();
+ aic_enable_i2s_bit_clock(false);
+
+ /* switch to new source */
+ cur_audio_source = source;
- if(enabled) {
- /* Set the new frequency; clock is enabled afterward */
- if(aic_i2s_set_mclk(X1000_CLK_SCLK_A, freq, mult))
- logf("WARNING: unachievable audio rate %d x %d!?", freq, mult);
+ if(source == AUDIO_SRC_PLAYBACK) {
+ /* power on DAC */
+ aic_set_external_codec(true);
+ aic_set_i2s_mode(AIC_I2S_MASTER_MODE);
+ ak4376_open();
+
+ /* apply the old settings */
+ audiohw_set_volume(cur_vol_l, cur_vol_r);
+ audiohw_set_filter_roll_off(cur_filter_roll_off);
+ set_ak_freqmode();
+ } else if(source == AUDIO_SRC_MIC) {
+ aic_set_external_codec(false);
+ aic_set_i2s_mode(AIC_I2S_SLAVE_MODE);
+ /* Note: Sampling frequency is irrelevant here */
+ aic_set_i2s_clock(X1000_CLK_EXCLK, 48000, 0);
+ aic_enable_i2s_bit_clock(true);
+
+ x1000_icodec_open();
+
+ /* configure the mic */
+ x1000_icodec_mic1_enable(true);
+ x1000_icodec_mic1_bias_enable(true);
+ x1000_icodec_mic1_configure(JZCODEC_MIC1_DIFFERENTIAL |
+ JZCODEC_MIC1_BIAS_2_08V);
+
+ /* configure the ADC */
+ x1000_icodec_adc_mic_sel(JZCODEC_MIC_SEL_ANALOG);
+ x1000_icodec_adc_highpass_filter(true);
+ x1000_icodec_adc_frequency(cur_fsel);
+ x1000_icodec_adc_enable(true);
+
+ /* configure the mixer to put mic input in both channels */
+ x1000_icodec_mixer_enable(true);
+ x1000_icodec_write(JZCODEC_MIX2, 0x10);
+
+ /* set gain and unmute */
+ audiohw_set_recvol(cur_recvol, 0, AUDIO_GAIN_MIC);
+ x1000_icodec_adc_mute(false);
} else {
- /* Shut off the clock */
- jz_writef(AIC_I2SCR, STPBK(1));
+ logf("bad audio input source: %d (flags: %x)", source, flags);
}
+}
+
+void audiohw_set_volume(int vol_l, int vol_r)
+{
+ cur_vol_l = vol_l;
+ cur_vol_r = vol_r;
- return mult;
+ if(cur_audio_source == AUDIO_SRC_PLAYBACK)
+ ak4376_set_volume(vol_l, vol_r);
+}
+
+void audiohw_set_filter_roll_off(int val)
+{
+ cur_filter_roll_off = val;
+
+ if(cur_audio_source == AUDIO_SRC_PLAYBACK)
+ ak4376_set_filter_roll_off(val);
+}
+
+void audiohw_set_frequency(int fsel)
+{
+ cur_fsel = fsel;
+
+ if(cur_audio_source == AUDIO_SRC_PLAYBACK)
+ set_ak_freqmode();
+ else
+ x1000_icodec_adc_frequency(fsel);
+}
+
+void audiohw_set_power_mode(int mode)
+{
+ cur_power_mode = mode;
+
+ if(cur_audio_source == AUDIO_SRC_PLAYBACK)
+ set_ak_freqmode();
+}
+
+static int round_step_up(int x, int step)
+{
+ int rem = x % step;
+ if(rem > 0)
+ rem -= step;
+ return x - rem;
+}
+
+void audiohw_set_recvol(int left, int right, int type)
+{
+ (void)right;
+
+ if(type == AUDIO_GAIN_MIC) {
+ cur_recvol = left;
+
+ if(cur_audio_source == AUDIO_SRC_MIC) {
+ int mic_gain = round_step_up(left, X1000_ICODEC_MIC_GAIN_STEP);
+ mic_gain = MIN(mic_gain, X1000_ICODEC_MIC_GAIN_MAX);
+ mic_gain = MAX(mic_gain, X1000_ICODEC_MIC_GAIN_MIN);
+
+ int adc_gain = left - mic_gain;
+ adc_gain = MIN(adc_gain, X1000_ICODEC_ADC_GAIN_MAX);
+ adc_gain = MAX(adc_gain, X1000_ICODEC_ADC_GAIN_MIN);
+
+ x1000_icodec_adc_gain(adc_gain);
+ x1000_icodec_mic1_gain(mic_gain);
+ }
+ }
+}
+
+void ak4376_set_pdn_pin(int level)
+{
+ gpio_set_level(GPIO_AK4376_POWER, level ? 1 : 0);
}
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/backlight-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/backlight-fiiom3k.c
index f02fcaaee8..a158f615e0 100644
--- a/firmware/target/mips/ingenic_x1000/fiiom3k/backlight-fiiom3k.c
+++ b/firmware/target/mips/ingenic_x1000/fiiom3k/backlight-fiiom3k.c
@@ -43,7 +43,6 @@ bool backlight_hw_init(void)
pwm_enable(BL_BTN_CHN);
backlight_hw_brightness(MAX_BRIGHTNESS_SETTING);
buttonlight_hw_brightness(MAX_BRIGHTNESS_SETTING);
- /* TODO: avoid buttonlight flicker when powering up the machine */
return true;
}
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c
index db5ece10b0..24daf2ef69 100644
--- a/firmware/target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c
+++ b/firmware/target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c
@@ -22,27 +22,21 @@
#include "button.h"
#include "kernel.h"
#include "backlight.h"
+#include "powermgmt.h"
#include "panic.h"
-#include "lcd.h"
+#include "axp-pmu.h"
+#include "ft6x06.h"
#include "gpio-x1000.h"
+#include "irq-x1000.h"
#include "i2c-x1000.h"
#include <string.h>
#include <stdbool.h>
#ifndef BOOTLOADER
+# include "lcd.h"
# include "font.h"
#endif
-#define FT_RST_PIN (1 << 15)
-#define FT_INT_PIN (1 << 12)
-#define ft_interrupt GPIOB12
-
-/* Touch event types */
-#define EVENT_NONE (-1)
-#define EVENT_PRESS 0
-#define EVENT_RELEASE 1
-#define EVENT_CONTACT 2
-
/* FSM states */
#define STATE_IDLE 0
#define STATE_PRESS 1
@@ -51,11 +45,11 @@
#define STATE_SCROLLING 4
/* Assume there's no active touch if no event is reported in this time */
-#define AUTORELEASE_TIME (10000 * OST_TICKS_PER_US)
+#define AUTORELEASE_TIME (40 * 1000 * OST_TICKS_PER_US)
/* If there's no significant motion on the scrollbar for this time,
* then report it as a button press instead */
-#define SCROLL_PRESS_TIME (100000 * OST_TICKS_PER_US)
+#define SCROLL_PRESS_TIME (400 * 1000 * OST_TICKS_PER_US)
/* If a press on the scrollbar moves more than this during SCROLL_PRESS_TIME,
* then we enter scrolling mode. */
@@ -68,17 +62,6 @@
/* Number of touch samples to smooth before reading */
#define TOUCH_SAMPLES 3
-static struct ft_driver {
- int i2c_cookie;
- i2c_descriptor i2c_desc;
- uint8_t raw_data[6];
- bool active;
-
- /* Number of pixels squared which must be moved before
- * a scrollbar pulse is generated */
- int scroll_thresh_sqr;
-} ftd;
-
static struct ft_state_machine {
/* Current button state, used by button_read_device() */
int buttons;
@@ -107,8 +90,27 @@ static struct ft_state_machine {
/* Current touch position */
int cur_x, cur_y;
+
+ /* Motion threshold squared, in 'pixels', required to trigger scrolling */
+ int scroll_thresh_sqr;
+
+ /* Touchpad enabled state */
+ bool active;
} fsm;
+/* coordinates below this are the left hand buttons,
+ * coordinates above this are part of the scrollbar */
+#define SCROLLSTRIP_LEFT_X 80
+
+/* top and bottom cutoffs for the center of the strip,
+ * divides it into top/middle/bottom zones */
+#define SCROLLSTRIP_TOP_Y 105
+#define SCROLLSTRIP_BOT_Y 185
+
+/* cutoffs for the menu/left button zones */
+#define MENUBUTTON_Y 80
+#define LEFTBUTTON_Y 190
+
static int touch_to_button(int x, int y)
{
if(x == 900) {
@@ -119,19 +121,19 @@ static int touch_to_button(int x, int y)
return BUTTON_RIGHT;
else
return 0;
- } else if(x < 80) {
+ } else if(x < SCROLLSTRIP_LEFT_X) {
/* Left strip */
- if(y < 80)
+ if(y < MENUBUTTON_Y)
return BUTTON_MENU;
- else if(y > 190)
+ else if(y > LEFTBUTTON_Y)
return BUTTON_LEFT;
else
return 0;
} else {
/* Middle strip */
- if(y < 100)
+ if(y < SCROLLSTRIP_TOP_Y)
return BUTTON_UP;
- else if(y > 220)
+ else if(y > SCROLLSTRIP_BOT_Y)
return BUTTON_DOWN;
else
return BUTTON_SELECT;
@@ -199,9 +201,9 @@ static void ft_start_report_or_scroll(void)
static void ft_step_state(uint32_t t, int evt, int tx, int ty)
{
/* Generate a release event automatically in case we missed it */
- if(evt == EVENT_NONE) {
+ if(evt == FT6x06_EVT_NONE) {
if(TICKS_SINCE(t, fsm.last_event_t) >= AUTORELEASE_TIME) {
- evt = EVENT_RELEASE;
+ evt = FT6x06_EVT_RELEASE;
tx = fsm.cur_x;
ty = fsm.cur_y;
}
@@ -209,7 +211,7 @@ static void ft_step_state(uint32_t t, int evt, int tx, int ty)
switch(fsm.state) {
case STATE_IDLE: {
- if(evt == EVENT_PRESS || evt == EVENT_CONTACT) {
+ if(evt == FT6x06_EVT_PRESS || evt == FT6x06_EVT_CONTACT) {
/* Move to REPORT or PRESS state */
if(ft_accum_touch(t, tx, ty))
ft_start_report_or_scroll();
@@ -219,10 +221,10 @@ static void ft_step_state(uint32_t t, int evt, int tx, int ty)
} break;
case STATE_PRESS: {
- if(evt == EVENT_RELEASE) {
+ if(evt == FT6x06_EVT_RELEASE) {
/* Ignore if the number of samples is too low */
ft_go_idle();
- } else if(evt == EVENT_PRESS || evt == EVENT_CONTACT) {
+ } else if(evt == FT6x06_EVT_PRESS || evt == FT6x06_EVT_CONTACT) {
/* Accumulate the touch position in the filter */
if(ft_accum_touch(t, tx, ty))
ft_start_report_or_scroll();
@@ -230,14 +232,14 @@ static void ft_step_state(uint32_t t, int evt, int tx, int ty)
} break;
case STATE_REPORT: {
- if(evt == EVENT_RELEASE)
+ if(evt == FT6x06_EVT_RELEASE)
ft_go_idle();
- else if(evt == EVENT_PRESS || evt == EVENT_CONTACT)
+ else if(evt == FT6x06_EVT_PRESS || evt == FT6x06_EVT_CONTACT)
ft_accum_touch(t, tx, ty);
} break;
case STATE_SCROLL_PRESS: {
- if(evt == EVENT_RELEASE) {
+ if(evt == FT6x06_EVT_RELEASE) {
/* This _should_ synthesize a button press.
*
* - ft_start_report() will set the button bit based on the
@@ -246,10 +248,10 @@ static void ft_step_state(uint32_t t, int evt, int tx, int ty)
*
* - The next button_read_device() will see the button bit
* and report it back to Rockbox, then step the FSM with
- * EVENT_NONE.
+ * FT6x06_EVT_NONE.
*
- * - The EVENT_NONE stepping will eventually autogenerate a
- * RELEASE event and restore the button state back to 0
+ * - The FT6x06_EVT_NONE stepping will eventually autogenerate
+ * a RELEASE event and restore the button state back to 0
*
* - There's a small logic hole in the REPORT state which
* could cause it to miss an immediately repeated PRESS
@@ -260,7 +262,7 @@ static void ft_step_state(uint32_t t, int evt, int tx, int ty)
break;
}
- if(evt == EVENT_PRESS || evt == EVENT_CONTACT)
+ if(evt == FT6x06_EVT_PRESS || evt == FT6x06_EVT_CONTACT)
ft_accum_touch(t, tx, ty);
int dx = fsm.cur_x - fsm.orig_x;
@@ -278,28 +280,33 @@ static void ft_step_state(uint32_t t, int evt, int tx, int ty)
} break;
case STATE_SCROLLING: {
- if(evt == EVENT_RELEASE) {
+ if(evt == FT6x06_EVT_RELEASE) {
ft_go_idle();
break;
}
- if(evt == EVENT_PRESS || evt == EVENT_CONTACT)
+ if(evt == FT6x06_EVT_PRESS || evt == FT6x06_EVT_CONTACT)
ft_accum_touch(t, tx, ty);
int dx = fsm.cur_x - fsm.orig_x;
int dy = fsm.cur_y - fsm.orig_y;
int dp = (dx*dx) + (dy*dy);
- if(dp >= ftd.scroll_thresh_sqr) {
- if(dy < 0) {
- queue_post(&button_queue, BUTTON_SCROLL_BACK, 0);
- } else {
- queue_post(&button_queue, BUTTON_SCROLL_FWD, 0);
+ if(dp >= fsm.scroll_thresh_sqr) {
+ /* avoid generating events if we're supposed to be inactive...
+ * should not be necessary but better to be safe. */
+ if(fsm.active) {
+ if(dy < 0) {
+ queue_post(&button_queue, BUTTON_SCROLL_BACK, 0);
+ } else {
+ queue_post(&button_queue, BUTTON_SCROLL_FWD, 0);
+ }
+
+ /* Poke the backlight */
+ backlight_on();
+ buttonlight_on();
+ reset_poweroff_timer();
}
- /* Poke the backlight */
- backlight_on();
- buttonlight_on();
-
fsm.orig_x = fsm.cur_x;
fsm.orig_y = fsm.cur_y;
}
@@ -311,18 +318,8 @@ static void ft_step_state(uint32_t t, int evt, int tx, int ty)
}
}
-static void ft_i2c_callback(int status, i2c_descriptor* desc)
+static void ft_event_cb(struct ft6x06_state* state)
{
- (void)desc;
- if(status != I2C_STATUS_OK)
- return;
-
- /* The panel is oriented such that its X axis is vertical,
- * so swap the axes for reporting */
- int evt = ftd.raw_data[1] >> 6;
- int ty = ftd.raw_data[2] | ((ftd.raw_data[1] & 0xf) << 8);
- int tx = ftd.raw_data[4] | ((ftd.raw_data[3] & 0xf) << 8);
-
/* TODO: convert the touch positions to linear positions.
*
* Points reported by the touch controller are distorted and non-linear,
@@ -330,35 +327,12 @@ static void ft_i2c_callback(int status, i2c_descriptor* desc)
* the middle of the touchpad than on the edges, so scrolling feels slow
* in the middle and faster near the edge.
*/
-
- ft_step_state(__ost_read32(), evt, tx, ty);
-}
-
-void ft_interrupt(void)
-{
- /* We don't care if this fails */
- i2c_async_queue(FT6x06_BUS, TIMEOUT_NOBLOCK, I2C_Q_ONCE,
- ftd.i2c_cookie, &ftd.i2c_desc);
+ struct ft6x06_point* pt = &state->points[0];
+ ft_step_state(__ost_read32(), pt->event, pt->pos_x, pt->pos_y);
}
static void ft_init(void)
{
- /* Initialize the driver state */
- ftd.i2c_cookie = i2c_async_reserve_cookies(FT6x06_BUS, 1);
- ftd.i2c_desc.slave_addr = FT6x06_ADDR;
- ftd.i2c_desc.bus_cond = I2C_START | I2C_STOP;
- ftd.i2c_desc.tran_mode = I2C_READ;
- ftd.i2c_desc.buffer[0] = &ftd.raw_data[5];
- ftd.i2c_desc.count[0] = 1;
- ftd.i2c_desc.buffer[1] = &ftd.raw_data[0];
- ftd.i2c_desc.count[1] = 5;
- ftd.i2c_desc.callback = ft_i2c_callback;
- ftd.i2c_desc.arg = 0;
- ftd.i2c_desc.next = NULL;
- ftd.raw_data[5] = 0x02;
- ftd.active = true;
- touchpad_set_sensitivity(DEFAULT_TOUCHPAD_SENSITIVITY_SETTING);
-
/* Initialize the state machine */
fsm.buttons = 0;
fsm.state = STATE_IDLE;
@@ -368,29 +342,39 @@ static void ft_init(void)
fsm.sum_x = fsm.sum_y = 0;
fsm.orig_x = fsm.orig_y = 0;
fsm.cur_x = fsm.cur_y = 0;
+ fsm.active = true;
+ touchpad_set_sensitivity(DEFAULT_TOUCHPAD_SENSITIVITY_SETTING);
/* Bring up I2C bus */
i2c_x1000_set_freq(FT6x06_BUS, I2C_FREQ_400K);
+ /* Driver init */
+ ft6x06_init();
+ ft6x06_set_event_cb(ft_event_cb);
+
/* Reset chip */
- gpio_config(GPIO_B, FT_RST_PIN|FT_INT_PIN, GPIO_OUTPUT(0));
+ gpio_set_level(GPIO_FT6x06_RESET, 0);
mdelay(5);
- gpio_out_level(GPIO_B, FT_RST_PIN, 1);
- gpio_config(GPIO_B, FT_INT_PIN, GPIO_IRQ_EDGE(0));
- gpio_enable_irq(GPIO_B, FT_INT_PIN);
+ gpio_set_level(GPIO_FT6x06_RESET, 1);
+
+ /* Configure the interrupt pin */
+ system_set_irq_handler(GPIO_TO_IRQ(GPIO_FT6x06_INTERRUPT),
+ ft6x06_irq_handler);
+ gpio_set_function(GPIO_FT6x06_INTERRUPT, GPIOF_IRQ_EDGE(0));
+ gpio_enable_irq(GPIO_FT6x06_INTERRUPT);
}
void touchpad_set_sensitivity(int level)
{
int pixels = 40;
pixels -= level;
- ftd.scroll_thresh_sqr = pixels * pixels;
+ fsm.scroll_thresh_sqr = pixels * pixels;
}
void touchpad_enable_device(bool en)
{
- i2c_reg_write1(FT6x06_BUS, FT6x06_ADDR, 0xa5, en ? 0 : 3);
- ftd.active = en;
+ ft6x06_enable(en);
+ fsm.active = en;
}
/* Value of headphone detect register */
@@ -402,16 +386,16 @@ static uint8_t hp_detect_reg = 0x00;
static int hp_detect_tmo_cb(struct timeout* tmo)
{
i2c_descriptor* d = (i2c_descriptor*)tmo->data;
- i2c_async_queue(AXP173_BUS, TIMEOUT_NOBLOCK, I2C_Q_ADD, 0, d);
+ i2c_async_queue(AXP_PMU_BUS, TIMEOUT_NOBLOCK, I2C_Q_ADD, 0, d);
return HPD_POLL_TIME;
}
static void hp_detect_init(void)
{
static struct timeout tmo;
- static const uint8_t gpio_reg = 0x94;
+ static const uint8_t gpio_reg = AXP192_REG_GPIOSTATE1;
static i2c_descriptor desc = {
- .slave_addr = AXP173_ADDR,
+ .slave_addr = AXP_PMU_ADDR,
.bus_cond = I2C_START | I2C_STOP,
.tran_mode = I2C_READ,
.buffer[0] = (void*)&gpio_reg,
@@ -423,12 +407,11 @@ static void hp_detect_init(void)
.next = NULL,
};
- /* Headphone detect is wired to an undocumented GPIO on the AXP173.
- * This sets it to input mode so we can see the pin state. */
- i2c_reg_write1(AXP173_BUS, AXP173_ADDR, 0x93, 0x01);
+ /* Headphone detect is wired to AXP192 GPIO: set it to input state */
+ i2c_reg_write1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP192_REG_GPIO2FUNCTION, 0x01);
/* Get an initial reading before startup */
- int r = i2c_reg_read1(AXP173_BUS, AXP173_ADDR, gpio_reg);
+ int r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, gpio_reg);
if(r >= 0)
hp_detect_reg = r;
@@ -439,10 +422,6 @@ static void hp_detect_init(void)
/* Rockbox interface */
void button_init_device(void)
{
- /* Configure physical button GPIOs */
- gpio_config(GPIO_A, (1 << 17) | (1 << 19), GPIO_INPUT);
- gpio_config(GPIO_B, (1 << 28) | (1 << 31), GPIO_INPUT);
-
/* Initialize touchpad */
ft_init();
@@ -453,7 +432,7 @@ void button_init_device(void)
int button_read_device(void)
{
int r = fsm.buttons;
- ft_step_state(__ost_read32(), EVENT_NONE, 0, 0);
+ ft_step_state(__ost_read32(), FT6x06_EVT_NONE, 0, 0);
/* Read GPIOs for physical buttons */
uint32_t a = REG_GPIO_PIN(GPIO_A);
@@ -465,10 +444,10 @@ int button_read_device(void)
if((b & (1 << 28)) == 0) r |= BUTTON_VOL_DOWN;
if((b & (1 << 31)) == 0) r |= BUTTON_POWER;
- return r;
+ return touchpad_filter(r);
}
-bool headphones_inserted()
+bool headphones_inserted(void)
{
return hp_detect_reg & 0x40 ? true : false;
}
@@ -489,6 +468,24 @@ bool dbg_fiiom3k_touchpad(void)
"IDLE", "PRESS", "REPORT", "SCROLL_PRESS", "SCROLLING"
};
+ /* definition of box used to represent the touchpad */
+ const int pad_w = LCD_WIDTH;
+ const int pad_h = LCD_HEIGHT;
+ const int box_h = pad_h - SYSFONT_HEIGHT*5;
+ const int box_w = pad_w * box_h / pad_h;
+ const int box_x = (LCD_WIDTH - box_w) / 2;
+ const int box_y = SYSFONT_HEIGHT * 9 / 2;
+
+ /* cutoffs converted to box coordinates */
+ const int ss_left_x = box_x + SCROLLSTRIP_LEFT_X * box_w / pad_w;
+ const int ss_top_y = box_y + SCROLLSTRIP_TOP_Y * box_h / pad_h;
+ const int ss_bot_y = box_y + SCROLLSTRIP_BOT_Y * box_h / pad_h;
+ const int menubtn_y = box_y + MENUBUTTON_Y * box_h / pad_h;
+ const int leftbtn_y = box_y + LEFTBUTTON_Y * box_h / pad_h;
+
+ bool draw_areas = true;
+ bool draw_border = true;
+
do {
int line = 0;
lcd_clear_display();
@@ -496,6 +493,26 @@ bool dbg_fiiom3k_touchpad(void)
lcd_putsf(0, line++, "button: %08x", fsm.buttons);
lcd_putsf(0, line++, "pos x: %4d orig x: %4d", fsm.cur_x, fsm.orig_x);
lcd_putsf(0, line++, "pos y: %4d orig y: %4d", fsm.cur_y, fsm.orig_y);
+
+ /* draw touchpad box borders */
+ if(draw_border)
+ lcd_drawrect(box_x, box_y, box_w, box_h);
+
+ /* draw crosshair */
+ int tx = box_x + fsm.cur_x * box_w / pad_w;
+ int ty = box_y + fsm.cur_y * box_h / pad_h;
+ lcd_hline(tx-2, tx+2, ty);
+ lcd_vline(tx, ty-2, ty+2);
+
+ /* draw the button areas */
+ if(draw_areas) {
+ lcd_vline(ss_left_x, box_y, box_y+box_h);
+ lcd_hline(ss_left_x, box_x+box_w, ss_top_y);
+ lcd_hline(ss_left_x, box_x+box_w, ss_bot_y);
+ lcd_hline(box_x, ss_left_x, menubtn_y);
+ lcd_hline(box_x, ss_left_x, leftbtn_y);
+ }
+
lcd_update();
} while(getbtn() != BUTTON_POWER);
return false;
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/gpio-target.h b/firmware/target/mips/ingenic_x1000/fiiom3k/gpio-target.h
new file mode 100644
index 0000000000..f580cd9167
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/fiiom3k/gpio-target.h
@@ -0,0 +1,26 @@
+/* Name Port Pins Function */
+DEFINE_PINGROUP(LCD_DATA, GPIO_A, 0xffff << 0, GPIOF_DEVICE(1))
+DEFINE_PINGROUP(LCD_CONTROL, GPIO_B, 0x1a << 16, GPIOF_DEVICE(1))
+DEFINE_PINGROUP(MSC0, GPIO_A, 0x3f << 20, GPIOF_DEVICE(1))
+DEFINE_PINGROUP(SFC, GPIO_A, 0x3f << 26, GPIOF_DEVICE(1))
+DEFINE_PINGROUP(I2S, GPIO_B, 0x1f << 0, GPIOF_DEVICE(1))
+DEFINE_PINGROUP(DMIC, GPIO_B, 3 << 21, GPIOF_DEVICE(0))
+DEFINE_PINGROUP(I2C0, GPIO_B, 3 << 23, GPIOF_DEVICE(0))
+DEFINE_PINGROUP(I2C1, GPIO_C, 3 << 26, GPIOF_DEVICE(0))
+DEFINE_PINGROUP(I2C2, GPIO_D, 3 << 0, GPIOF_DEVICE(1))
+
+/* Name Pin Function */
+DEFINE_GPIO(AK4376_POWER, GPIO_PA(16), GPIOF_OUTPUT(0))
+DEFINE_GPIO(BTN_PLAY, GPIO_PA(17), GPIOF_INPUT)
+DEFINE_GPIO(BTN_VOL_UP, GPIO_PA(19), GPIOF_INPUT)
+DEFINE_GPIO(MSC0_CD, GPIO_PB(6), GPIOF_INPUT)
+DEFINE_GPIO(USB_ID, GPIO_PB(7), GPIOF_INPUT)
+DEFINE_GPIO(AXP_IRQ, GPIO_PB(10), GPIOF_INPUT)
+DEFINE_GPIO(USB_DETECT, GPIO_PB(11), GPIOF_INPUT)
+DEFINE_GPIO(FT6x06_INTERRUPT, GPIO_PB(12), GPIOF_INPUT)
+DEFINE_GPIO(FT6x06_RESET, GPIO_PB(15), GPIOF_OUTPUT(0))
+DEFINE_GPIO(LCD_RD, GPIO_PB(16), GPIOF_OUTPUT(1))
+DEFINE_GPIO(LCD_CE, GPIO_PB(18), GPIOF_OUTPUT(1))
+DEFINE_GPIO(USB_DRVVBUS, GPIO_PB(25), GPIOF_OUTPUT(0))
+DEFINE_GPIO(BTN_VOL_DOWN, GPIO_PB(28), GPIOF_INPUT)
+DEFINE_GPIO(BTN_POWER, GPIO_PB(31), GPIOF_INPUT)
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/i2c-target.h b/firmware/target/mips/ingenic_x1000/fiiom3k/i2c-target.h
index a389d2af42..1e8ebfbb15 100644
--- a/firmware/target/mips/ingenic_x1000/fiiom3k/i2c-target.h
+++ b/firmware/target/mips/ingenic_x1000/fiiom3k/i2c-target.h
@@ -31,7 +31,7 @@
#define FT6x06_BUS 1
#define FT6x06_ADDR 0x38
-#define AXP173_BUS 2
-#define AXP173_ADDR 0x34
+#define AXP_PMU_BUS 2
+#define AXP_PMU_ADDR 0x34
#endif /* __I2C_TARGET_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c
deleted file mode 100644
index cdd7276bee..0000000000
--- a/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c
+++ /dev/null
@@ -1,195 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2021 Aidan MacDonald
- *
- * 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 "installer.h"
-#include "nand-x1000.h"
-#include "core_alloc.h"
-#include "file.h"
-
-#define INSTALL_SUCCESS 0
-#define ERR_FLASH_OPEN_FAILED (-1)
-#define ERR_FLASH_ENABLE_WP_FAILED (-2)
-#define ERR_FLASH_DISABLE_WP_FAILED (-3)
-#define ERR_FLASH_ERASE_FAILED (-4)
-#define ERR_FLASH_WRITE_FAILED (-5)
-#define ERR_FLASH_READ_FAILED (-6)
-#define ERR_OUT_OF_MEMORY (-7)
-#define ERR_CANNOT_READ_FILE (-8)
-#define ERR_CANNOT_WRITE_FILE (-9)
-#define ERR_WRONG_SIZE (-10)
-
-#define BOOT_IMAGE_SIZE (128 * 1024)
-
-static int install_from_buffer(const void* buf)
-{
- if(nand_open())
- return ERR_FLASH_OPEN_FAILED;
-
- int status = INSTALL_SUCCESS;
-
- if(nand_enable_writes(true)) {
- status = ERR_FLASH_DISABLE_WP_FAILED;
- goto _exit;
- }
-
- if(nand_erase_bytes(0, BOOT_IMAGE_SIZE)) {
- status = ERR_FLASH_ERASE_FAILED;
- goto _exit;
- }
-
- if(nand_write_bytes(0, BOOT_IMAGE_SIZE, buf)) {
- status = ERR_FLASH_WRITE_FAILED;
- goto _exit;
- }
-
- if(nand_enable_writes(false)) {
- status = ERR_FLASH_ENABLE_WP_FAILED;
- goto _exit;
- }
-
- _exit:
- nand_close();
- return status;
-}
-
-static int dump_to_buffer(void* buf)
-{
- if(nand_open())
- return ERR_FLASH_OPEN_FAILED;
-
- int status = INSTALL_SUCCESS;
-
- if(nand_read_bytes(0, BOOT_IMAGE_SIZE, buf)) {
- status = ERR_FLASH_READ_FAILED;
- goto _exit;
- }
-
- _exit:
- nand_close();
- return status;
-}
-
-int install_bootloader(const char* path)
-{
- /* Allocate memory to hold image */
- int handle = core_alloc("boot_image", BOOT_IMAGE_SIZE);
- if(handle < 0)
- return ERR_OUT_OF_MEMORY;
-
- int status = INSTALL_SUCCESS;
- void* buffer = core_get_data(handle);
-
- /* Open the boot image */
- int fd = open(path, O_RDONLY);
- if(fd < 0) {
- status = ERR_CANNOT_READ_FILE;
- goto _exit;
- }
-
- /* Check file size */
- off_t fsize = filesize(fd);
- if(fsize != BOOT_IMAGE_SIZE) {
- status = ERR_WRONG_SIZE;
- goto _exit;
- }
-
- /* Read the file into the buffer */
- ssize_t cnt = read(fd, buffer, BOOT_IMAGE_SIZE);
- if(cnt != BOOT_IMAGE_SIZE) {
- status = ERR_CANNOT_READ_FILE;
- goto _exit;
- }
-
- /* Perform the installation */
- status = install_from_buffer(buffer);
-
- _exit:
- if(fd >= 0)
- close(fd);
- core_free(handle);
- return status;
-}
-
-/* Dump the current bootloader to a file */
-int dump_bootloader(const char* path)
-{
- /* Allocate memory to hold image */
- int handle = core_alloc("boot_image", BOOT_IMAGE_SIZE);
- if(handle < 0)
- return -1;
-
- /* Read data from flash */
- int fd = -1;
- void* buffer = core_get_data(handle);
- int status = dump_to_buffer(buffer);
- if(status)
- goto _exit;
-
- /* Open file */
- fd = open(path, O_CREAT|O_TRUNC|O_WRONLY);
- if(fd < 0) {
- status = ERR_CANNOT_WRITE_FILE;
- goto _exit;
- }
-
- /* Write data to file */
- ssize_t cnt = write(fd, buffer, BOOT_IMAGE_SIZE);
- if(cnt != BOOT_IMAGE_SIZE) {
- status = ERR_CANNOT_WRITE_FILE;
- goto _exit;
- }
-
- _exit:
- if(fd >= 0)
- close(fd);
- core_free(handle);
- return status;
-}
-
-const char* installer_strerror(int rc)
-{
- switch(rc) {
- case INSTALL_SUCCESS:
- return "Success";
- case ERR_FLASH_OPEN_FAILED:
- return "Can't open flash device";
- case ERR_FLASH_ENABLE_WP_FAILED:
- return "Couldn't re-enable write protect";
- case ERR_FLASH_DISABLE_WP_FAILED:
- return "Can't disable write protect";
- case ERR_FLASH_ERASE_FAILED:
- return "Flash erase failed";
- case ERR_FLASH_WRITE_FAILED:
- return "Flash write error";
- case ERR_FLASH_READ_FAILED:
- return "Flash read error";
- case ERR_OUT_OF_MEMORY:
- return "Out of memory";
- case ERR_CANNOT_READ_FILE:
- return "Error reading file";
- case ERR_CANNOT_WRITE_FILE:
- return "Error writing file";
- case ERR_WRONG_SIZE:
- return "Wrong file size";
- default:
- return "Unknown error";
- }
-}
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/lcd-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/lcd-fiiom3k.c
index 96f794d7df..29e72286ff 100644
--- a/firmware/target/mips/ingenic_x1000/fiiom3k/lcd-fiiom3k.c
+++ b/firmware/target/mips/ingenic_x1000/fiiom3k/lcd-fiiom3k.c
@@ -25,9 +25,6 @@
#include "gpio-x1000.h"
#include "system.h"
-#define CS_PIN (1 << 18)
-#define RD_PIN (1 << 16)
-
static const uint32_t fiio_lcd_cmd_enable[] = {
/* Software reset */
LCD_INSTR_CMD, 0x01,
@@ -169,17 +166,27 @@ const struct lcd_tgt_config lcd_tgt_config = {
void lcd_tgt_enable(bool enable)
{
if(enable) {
- gpio_config(GPIO_A, 0xffff, GPIO_DEVICE(1));
- gpio_config(GPIO_B, 0x1f << 16, GPIO_DEVICE(1));
- gpio_config(GPIO_B, CS_PIN|RD_PIN, GPIO_OUTPUT(1));
+ /* reset controller, probably */
+ gpio_set_level(GPIO_LCD_CE, 1);
+ gpio_set_level(GPIO_LCD_RD, 1);
mdelay(5);
- gpio_out_level(GPIO_B, CS_PIN, 0);
+ gpio_set_level(GPIO_LCD_CE, 0);
+
+ /* set the clock whatever it is... */
lcd_set_clock(X1000_CLK_SCLK_A, 30000000);
+
+ /* program the initial configuration */
lcd_exec_commands(&fiio_lcd_cmd_enable[0]);
} else {
+ /* go to sleep mode first */
lcd_exec_commands(&fiio_lcd_cmd_sleep[0]);
- mdelay(115); /* ensure we wait a total of 120ms before power off */
- gpio_config(GPIO_B, CS_PIN|RD_PIN, 0);
+
+ /* ensure we wait a total of 120ms before power off */
+ mdelay(115);
+
+ /* this is intended to power off the panel but I'm not sure it does */
+ gpio_set_level(GPIO_LCD_CE, 0);
+ gpio_set_level(GPIO_LCD_RD, 0);
}
}
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/nand-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/nand-fiiom3k.c
deleted file mode 100644
index 7c8a306bae..0000000000
--- a/firmware/target/mips/ingenic_x1000/fiiom3k/nand-fiiom3k.c
+++ /dev/null
@@ -1,53 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2021 Aidan MacDonald
- *
- * 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 "nand-x1000.h"
-#include "nand-target.h"
-#include "sfc-x1000.h"
-
-/* Unbelievably FiiO has completely disabled the use of ECC for this chip
- * in their Linux kernel, even though it has perfectly good spare areas.
- * There's no internal ECC either.
- *
- * Using nanddump to read the spare areas reveals they're filled with 0xff,
- * and the publicly released Linux source has the ecc_strength set to 0.
- */
-static const nand_chip_data ato25d1ga = {
- .name = "ATO25D1GA",
- .mf_id = 0x9b,
- .dev_id = 0x12,
- .dev_conf = NAND_INIT_SFC_DEV_CONF,
- /* XXX: datasheet says 104 MHz but FiiO seems to run this at 150 MHz.
- * Didn't find any issues doing this so might as well keep the behavior.
- */
- .clock_freq = NAND_INIT_CLOCK_SPEED,
- .block_size = 64,
- .page_size = 2048,
- .spare_size = 64,
- .rowaddr_width = 3,
- .coladdr_width = 2,
- .flags = NANDCHIP_FLAG_QUAD,
-};
-
-const nand_chip_desc target_nand_chip_descs[] = {
- {&ato25d1ga, &nand_chip_ops_std},
- {NULL, NULL},
-};
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c
index b4923835aa..840be36a75 100644
--- a/firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c
+++ b/firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c
@@ -23,9 +23,11 @@
#include "adc.h"
#include "system.h"
#include "kernel.h"
-#include "axp173.h"
+#ifdef HAVE_USB_CHARGING_ENABLE
+# include "usb_core.h"
+#endif
+#include "axp-pmu.h"
#include "i2c-x1000.h"
-#include "gpio-x1000.h"
const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] =
{
@@ -50,30 +52,45 @@ const unsigned short percent_to_volt_charge[11] =
3485, 3780, 3836, 3857, 3890, 3930, 3986, 4062, 4158, 4185, 4196
};
-#define AXP173_IRQ_PORT GPIO_B
-#define AXP173_IRQ_PIN (1 << 10)
-
void power_init(void)
{
/* Initialize driver */
i2c_x1000_set_freq(2, I2C_FREQ_400K);
- axp173_init();
+ axp_init();
/* Set lowest sample rate */
- axp173_adc_set_rate(AXP173_ADC_RATE_25HZ);
+ axp_adc_set_rate(AXP_ADC_RATE_25HZ);
- /* Ensure battery voltage ADC is enabled */
- int bits = axp173_adc_get_enabled();
- bits |= (1 << ADC_BATTERY_VOLTAGE);
- axp173_adc_set_enabled(bits);
+ /* Enable required ADCs */
+ axp_adc_set_enabled(
+ (1 << ADC_BATTERY_VOLTAGE) |
+ (1 << ADC_CHARGE_CURRENT) |
+ (1 << ADC_DISCHARGE_CURRENT) |
+ (1 << ADC_VBUS_VOLTAGE) |
+ (1 << ADC_VBUS_CURRENT) |
+ (1 << ADC_INTERNAL_TEMP) |
+ (1 << ADC_APS_VOLTAGE));
/* Turn on all power outputs */
- i2c_reg_modify1(AXP173_BUS, AXP173_ADDR, 0x12, 0, 0x5f, NULL);
- i2c_reg_modify1(AXP173_BUS, AXP173_ADDR, 0x80, 0, 0xc0, NULL);
+ i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
+ AXP_REG_PWROUTPUTCTRL2, 0, 0x5f, NULL);
+ i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
+ AXP_REG_DCDCWORKINGMODE, 0, 0xc0, NULL);
+
+ /* Set the default charging current. This is the same as the
+ * OF's setting, although it's not strictly within the USB spec. */
+ axp_set_charge_current(780);
/* Short delay to give power outputs time to stabilize */
- mdelay(5);
+ mdelay(200);
+}
+
+#ifdef HAVE_USB_CHARGING_ENABLE
+void usb_charging_maxcurrent_change(int maxcurrent)
+{
+ axp_set_charge_current(maxcurrent);
}
+#endif
void adc_init(void)
{
@@ -81,17 +98,26 @@ void adc_init(void)
void power_off(void)
{
- /* Set the shutdown bit */
- i2c_reg_setbit1(AXP173_BUS, AXP173_ADDR, 0x32, 7, 1, NULL);
+ axp_power_off();
while(1);
}
bool charging_state(void)
{
- return axp173_battery_status() == AXP173_BATT_CHARGING;
+ return axp_battery_status() == AXP_BATT_CHARGING;
}
int _battery_voltage(void)
{
- return axp173_adc_read(ADC_BATTERY_VOLTAGE);
+ return axp_adc_read(ADC_BATTERY_VOLTAGE);
+}
+
+#if CONFIG_BATTERY_MEASURE & CURRENT_MEASURE
+int _battery_current(void)
+{
+ if(charging_state())
+ return axp_adc_read(ADC_CHARGE_CURRENT);
+ else
+ return axp_adc_read(ADC_DISCHARGE_CURRENT);
}
+#endif
diff --git a/firmware/target/mips/ingenic_x1000/gpio-x1000.c b/firmware/target/mips/ingenic_x1000/gpio-x1000.c
index a47865e397..0ebe424566 100644
--- a/firmware/target/mips/ingenic_x1000/gpio-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/gpio-x1000.c
@@ -20,65 +20,93 @@
****************************************************************************/
#include "gpio-x1000.h"
-#include "kernel.h"
-#ifndef BOOTLOADER_SPL
-struct mutex gpio_z_mutex;
+static const struct gpio_setting gpio_settings[PIN_COUNT] INITDATA_ATTR = {
+#define DEFINE_GPIO(_name, _gpio, _func) \
+ {.gpio = _gpio, .func = _func},
+#define DEFINE_PINGROUP(...)
+#include "gpio-target.h"
+#undef DEFINE_GPIO
+#undef DEFINE_PINGROUP
+};
+
+static const struct pingroup_setting pingroup_settings[PINGROUP_COUNT] INITDATA_ATTR = {
+#define DEFINE_GPIO(...)
+#define DEFINE_PINGROUP(_name, _port, _pins, _func) \
+ {.port = _port, .pins = _pins, .func = _func},
+#include "gpio-target.h"
+#undef DEFINE_GPIO
+#undef DEFINE_PINGROUP
+};
+
+#if 0 /* not needed for the time being */
+static const char* const gpio_names[PIN_COUNT] = {
+#define DEFINE_GPIO(_name, ...) #_name,
+#define DEFINE_PINGROUP(...)
+#include "gpio-target.h"
+#undef DEFINE_GPIO
+#undef DEFINE_PINGROUP
+};
+
+static const char* const pingroup_names[PINGROUP_COUNT] = {
+#define DEFINE_GPIO(...)
+#define DEFINE_PINGROUP(_name, ...) #_name,
+#include "gpio-target.h"
+#undef DEFINE_GPIO
+#undef DEFINE_PINGROUP
+};
#endif
void gpio_init(void)
{
-#ifndef BOOTLOADER_SPL
- mutex_init(&gpio_z_mutex);
-#endif
+ /* Apply all initial GPIO settings */
+ for(int i = 0; i < PINGROUP_COUNT; ++i) {
+ const struct pingroup_setting* d = &pingroup_settings[i];
+ if(d->pins != 0)
+ gpioz_configure(d->port, d->pins, d->func);
+ }
- /* Set all pins to input state */
- for(int i = 0; i < 4; ++i) {
- jz_clr(GPIO_INT(GPIO_Z), 0xffffffff);
- jz_set(GPIO_MSK(GPIO_Z), 0xffffffff);
- jz_set(GPIO_PAT1(GPIO_Z), 0xffffffff);
- jz_clr(GPIO_PAT0(GPIO_Z), 0xffffffff);
- REG_GPIO_Z_GID2LD = i;
+ for(int i = 0; i < PIN_COUNT; ++i) {
+ const struct gpio_setting* d = &gpio_settings[i];
+ if(d->gpio != GPIO_NONE)
+ gpioz_configure(GPION_PORT(d->gpio), GPION_MASK(d->gpio), d->func);
}
- /* Clear flag and disable pull resistor */
+ /* Any GPIO pins left in an IRQ trigger state need to be switched off,
+ * because the drivers won't be ready to handle the interrupts until they
+ * get initialized later in the boot. */
for(int i = 0; i < 4; ++i) {
- jz_clr(GPIO_FLAG(i), 0xffffffff);
- jz_set(GPIO_PULL(i), 0xffffffff);
+ uint32_t intbits = REG_GPIO_INT(i);
+ if(intbits) {
+ gpioz_configure(i, intbits, GPIOF_INPUT);
+ jz_clr(GPIO_FLAG(i), intbits);
+ }
}
}
-void gpio_lock(void)
+void gpioz_configure(int port, uint32_t pins, int func)
{
-#ifndef BOOTLOADER_SPL
- mutex_lock(&gpio_z_mutex);
-#endif
-}
+ uint32_t intr = REG_GPIO_INT(port);
+ uint32_t mask = REG_GPIO_MSK(port);
+ uint32_t pat1 = REG_GPIO_PAT1(port);
+ uint32_t pat0 = REG_GPIO_PAT0(port);
-void gpio_unlock(void)
-{
-#ifndef BOOTLOADER_SPL
- mutex_unlock(&gpio_z_mutex);
-#endif
-}
-
-void gpio_config(int port, unsigned pinmask, int func)
-{
- unsigned intr = REG_GPIO_INT(port);
- unsigned mask = REG_GPIO_MSK(port);
- unsigned pat1 = REG_GPIO_PAT1(port);
- unsigned pat0 = REG_GPIO_PAT0(port);
-
- gpio_lock();
- if(func & 8) jz_set(GPIO_INT(GPIO_Z), (intr & pinmask) ^ pinmask);
- else jz_clr(GPIO_INT(GPIO_Z), (~intr & pinmask) ^ pinmask);
- if(func & 4) jz_set(GPIO_MSK(GPIO_Z), (mask & pinmask) ^ pinmask);
- else jz_clr(GPIO_MSK(GPIO_Z), (~mask & pinmask) ^ pinmask);
- if(func & 2) jz_set(GPIO_PAT1(GPIO_Z), (pat1 & pinmask) ^ pinmask);
- else jz_clr(GPIO_PAT1(GPIO_Z), (~pat1 & pinmask) ^ pinmask);
- if(func & 1) jz_set(GPIO_PAT0(GPIO_Z), (pat0 & pinmask) ^ pinmask);
- else jz_clr(GPIO_PAT0(GPIO_Z), (~pat0 & pinmask) ^ pinmask);
+ /* Note: GPIO Z has _only_ set and clear registers, which are used to
+ * atomically manipulate the selected GPIO port when we write GID2LD.
+ * So there's not really any direct setting or clearing going on here...
+ */
+ if(func & GPIO_F_INT) jz_set(GPIO_INT(GPIO_Z), (intr & pins) ^ pins);
+ else jz_clr(GPIO_INT(GPIO_Z), (~intr & pins) ^ pins);
+ if(func & GPIO_F_MASK) jz_set(GPIO_MSK(GPIO_Z), (mask & pins) ^ pins);
+ else jz_clr(GPIO_MSK(GPIO_Z), (~mask & pins) ^ pins);
+ if(func & GPIO_F_PAT1) jz_set(GPIO_PAT1(GPIO_Z), (pat1 & pins) ^ pins);
+ else jz_clr(GPIO_PAT1(GPIO_Z), (~pat1 & pins) ^ pins);
+ if(func & GPIO_F_PAT0) jz_set(GPIO_PAT0(GPIO_Z), (pat0 & pins) ^ pins);
+ else jz_clr(GPIO_PAT0(GPIO_Z), (~pat0 & pins) ^ pins);
REG_GPIO_Z_GID2LD = port;
- gpio_unlock();
- gpio_set_pull(port, pinmask, func & 16);
+
+ if(func & GPIO_F_PULL)
+ jz_set(GPIO_PULL(port), pins);
+ else
+ jz_clr(GPIO_PULL(port), pins);
}
diff --git a/firmware/target/mips/ingenic_x1000/gpio-x1000.h b/firmware/target/mips/ingenic_x1000/gpio-x1000.h
index f1a65b37b5..310a457ab5 100644
--- a/firmware/target/mips/ingenic_x1000/gpio-x1000.h
+++ b/firmware/target/mips/ingenic_x1000/gpio-x1000.h
@@ -22,35 +22,8 @@
#ifndef __GPIO_X1000_H__
#define __GPIO_X1000_H__
-/* GPIO API
- * --------
- *
- * To assign a new function to a GPIO, call gpio_config(). This uses the
- * hardware's GPIO Z facility to atomically most GPIO registers at once,
- * so it can be used to make any state transition safely. Since GPIO Z is
- * protected by a mutex, you can't call gpio_config() from interrupt context.
- *
- * If you need to use GPIO Z directly, then use gpio_lock() and gpio_unlock()
- * to acquire the mutex.
- *
- * Depending on the current GPIO state, certain state transitions are safe to
- * perform without locking, as they only change one register:
- *
- * - for pins in GPIO_OUTPUT state:
- * - use gpio_out_level() to change the output level
- *
- * - for pins in GPIO_IRQ_LEVEL or GPIO_IRQ_EDGE state:
- * - use gpio_irq_level() to change the trigger level
- * - use gpio_irq_mask() to mask/unmask the IRQ
- *
- * - for pins in GPIO_DEVICE or GPIO_INPUT state:
- * - no special transitions allowed
- *
- * - in all states:
- * - use gpio_set_pull() to change the pull-up/pull-down state
- */
-
#include "x1000/gpio.h"
+#include "config.h"
/* GPIO port numbers */
#define GPIO_A 0
@@ -67,44 +40,132 @@
#define GPIO_F_PAT0 1
/* GPIO function numbers */
-#define GPIO_DEVICE(i) ((i)&3)
-#define GPIO_OUTPUT(i) (0x4|((i)&1))
-#define GPIO_INPUT 0x16
-#define GPIO_IRQ_LEVEL(i) (0x1c|((i)&1))
-#define GPIO_IRQ_EDGE(i) (0x1e|((i)&1))
-
-extern void gpio_init(void);
-extern void gpio_lock(void);
-extern void gpio_unlock(void);
-extern void gpio_config(int port, unsigned pinmask, int func);
-
-static inline void gpio_out_level(int port, unsigned pinmask, int level)
+#define GPIOF_DEVICE(i) ((i)&3)
+#define GPIOF_OUTPUT(i) (0x4|((i)&1))
+#define GPIOF_INPUT 0x16
+#define GPIOF_IRQ_LEVEL(i) (0x1c|((i)&1))
+#define GPIOF_IRQ_EDGE(i) (0x1e|((i)&1))
+
+/* GPIO pin numbers */
+#define GPION_CREATE(port, pin) ((((port) & 3) << 5) | ((pin) & 0x1f))
+#define GPION_PORT(gpio) (((gpio) >> 5) & 3)
+#define GPION_PIN(gpio) ((gpio) & 0x1f)
+#define GPION_MASK(gpio) (1u << GPION_PIN(gpio))
+
+/* Easy pin number macros */
+#define GPIO_PA(x) GPION_CREATE(GPIO_A, x)
+#define GPIO_PB(x) GPION_CREATE(GPIO_B, x)
+#define GPIO_PC(x) GPION_CREATE(GPIO_C, x)
+#define GPIO_PD(x) GPION_CREATE(GPIO_D, x)
+
+/* GPIO number to IRQ number (need to include "irq-x1000.h") */
+#define GPIO_TO_IRQ(gpio) IRQ_GPIO(GPION_PORT(gpio), GPION_PIN(gpio))
+
+/* Pingroup settings are used for system devices */
+struct pingroup_setting {
+ int port;
+ uint32_t pins;
+ int func;
+};
+
+/* GPIO settings are used for single pins under software control */
+struct gpio_setting {
+ int gpio;
+ int func;
+};
+
+/* Target pins are defined as GPIO_XXX constants usable with the GPIO API */
+enum {
+#define DEFINE_GPIO(_name, _gpio, _func) GPIO_##_name = _gpio,
+#define DEFINE_PINGROUP(...)
+#include "gpio-target.h"
+#undef DEFINE_GPIO
+#undef DEFINE_PINGROUP
+ GPIO_NONE = -1,
+};
+
+/* These are pin IDs which index gpio_settings */
+enum {
+#define DEFINE_GPIO(_name, ...) PIN_##_name,
+#define DEFINE_PINGROUP(...)
+#include "gpio-target.h"
+#undef DEFINE_GPIO
+#undef DEFINE_PINGROUP
+ PIN_COUNT,
+};
+
+/* Pingroup IDs which index pingroup_settings */
+enum {
+#define DEFINE_GPIO(...)
+#define DEFINE_PINGROUP(_name, ...) PINGROUP_##_name,
+#include "gpio-target.h"
+#undef DEFINE_GPIO
+#undef DEFINE_PINGROUP
+ PINGROUP_COUNT,
+};
+
+/* called at early init to set up GPIOs */
+extern void gpio_init(void) INIT_ATTR;
+
+/* Use GPIO Z to reconfigure several pins atomically */
+extern void gpioz_configure(int port, uint32_t pins, int func);
+
+static inline void gpio_set_function(int gpio, int func)
+{
+ gpioz_configure(GPION_PORT(gpio), GPION_MASK(gpio), func);
+}
+
+static inline int gpio_get_level(int gpio)
+{
+ return REG_GPIO_PIN(GPION_PORT(gpio)) & GPION_MASK(gpio) ? 1 : 0;
+}
+
+static inline void gpio_set_level(int gpio, int value)
{
- if(level)
- jz_set(GPIO_PAT0(port), pinmask);
+ if(value)
+ jz_set(GPIO_PAT0(GPION_PORT(gpio)), GPION_MASK(gpio));
else
- jz_clr(GPIO_PAT0(port), pinmask);
+ jz_clr(GPIO_PAT0(GPION_PORT(gpio)), GPION_MASK(gpio));
}
-#define gpio_irq_level gpio_out_level
+static inline void gpio_set_pull(int gpio, int state)
+{
+ if(state)
+ jz_set(GPIO_PULL(GPION_PORT(gpio)), GPION_MASK(gpio));
+ else
+ jz_clr(GPIO_PULL(GPION_PORT(gpio)), GPION_MASK(gpio));
+}
-static inline void gpio_irq_mask(int port, unsigned pinmask, int masked)
+static inline void gpio_mask_irq(int gpio, int mask)
{
- if(masked)
- jz_set(GPIO_MSK(port), pinmask);
+ if(mask)
+ jz_set(GPIO_MSK(GPION_PORT(gpio)), GPION_MASK(gpio));
else
- jz_clr(GPIO_MSK(port), pinmask);
+ jz_clr(GPIO_MSK(GPION_PORT(gpio)), GPION_MASK(gpio));
}
-#define gpio_enable_irq(port, pinmask) gpio_irq_mask((port), (pinmask), 0)
-#define gpio_disable_irq(port, pinmask) gpio_irq_mask((port), (pinmask), 1)
+#define gpio_set_irq_level gpio_set_level
+#define gpio_enable_irq(gpio) gpio_mask_irq((gpio), 0)
+#define gpio_disable_irq(gpio) gpio_mask_irq((gpio), 1)
-static inline void gpio_set_pull(int port, unsigned pinmask, int state)
+/* Helper function for edge-triggered IRQs when you want to get an
+ * interrupt on both the rising and falling edges. The hardware can
+ * only be set up to interrupt on one edge, so interrupt handlers
+ * can call this function to flip the trigger to the other edge.
+ *
+ * Despite the name, this doesn't depend on the currently set edge,
+ * it just reads the GPIO state and sets up an edge trigger to detect
+ * a change to the other state -- if some transitions were missed the
+ * IRQ trigger may remain unchanged.
+ *
+ * It can be safely used to initialize the IRQ level.
+ */
+static inline void gpio_flip_edge_irq(int gpio)
{
- if(state)
- jz_set(GPIO_PULL(port), pinmask);
+ if(gpio_get_level(gpio))
+ gpio_set_irq_level(gpio, 0);
else
- jz_clr(GPIO_PULL(port), pinmask);
+ gpio_set_irq_level(gpio, 1);
}
#endif /* __GPIO_X1000_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/i2c-x1000.c b/firmware/target/mips/ingenic_x1000/i2c-x1000.c
index 8bf606227b..6a5d2e08d2 100644
--- a/firmware/target/mips/ingenic_x1000/i2c-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/i2c-x1000.c
@@ -25,7 +25,6 @@
#include "kernel.h"
#include "panic.h"
#include "logf.h"
-#include "gpio-x1000.h"
#include "clk-x1000.h"
#include "irq-x1000.h"
#include "x1000/i2c.h"
@@ -375,18 +374,6 @@ void i2c_init(void)
/* Stuff only required during initialization is below, basically the same as
* the old driver except for how the IRQs are initially set up. */
-static const struct {
- int port;
- unsigned pins;
- int func;
-} i2c_x1000_gpio_data[] = {
- {GPIO_B, 3 << 23, GPIO_DEVICE(0)},
- {GPIO_C, 3 << 26, GPIO_DEVICE(0)},
- /* Note: I2C1 is also on the following pins (normally used by LCD) */
- /* {GPIO_A, 3 << 0, GPIO_DEVICE(2)}, */
- {GPIO_D, 3 << 0, GPIO_DEVICE(1)},
-};
-
static void i2c_x1000_gate(int chn, int gate)
{
switch(chn) {
@@ -468,11 +455,6 @@ void i2c_x1000_set_freq(int chn, int freq)
jz_write(I2C_FHCNT(chn), t_HIGH);
}
- /* Claim pins */
- gpio_config(i2c_x1000_gpio_data[chn].port,
- i2c_x1000_gpio_data[chn].pins,
- i2c_x1000_gpio_data[chn].func);
-
/* Enable the controller */
i2c_x1000_enable(chn);
}
diff --git a/firmware/target/mips/ingenic_x1000/i2c-x1000.h b/firmware/target/mips/ingenic_x1000/i2c-x1000.h
index e76624d511..9b9ba5e088 100644
--- a/firmware/target/mips/ingenic_x1000/i2c-x1000.h
+++ b/firmware/target/mips/ingenic_x1000/i2c-x1000.h
@@ -27,7 +27,7 @@
#define I2C_FREQ_100K 100000
#define I2C_FREQ_400K 400000
-extern void i2c_init(void);
+extern void i2c_init(void) INIT_ATTR;
/* Configure the I2C controller prior to use.
*
diff --git a/firmware/target/mips/ingenic_x1000/installer-x1000.c b/firmware/target/mips/ingenic_x1000/installer-x1000.c
new file mode 100644
index 0000000000..d2f9e4e5e0
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/installer-x1000.c
@@ -0,0 +1,341 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 "installer-x1000.h"
+#include "nand-x1000.h"
+#include "core_alloc.h"
+#include "file.h"
+#include "microtar-rockbox.h"
+#include <stddef.h>
+
+struct update_part {
+ const char* filename;
+ size_t offset;
+ size_t length;
+};
+
+/* Parts of the flash to update. The offset and length are given in bytes,
+ * offset relative to start of flash. The region's new contents are given
+ * by the named file inside the update archive. If any file is missing, the
+ * update will fail. (gracefully! nothing is written unless the package has
+ * all its components)
+ *
+ * If the update file is smaller than the region size, unused space at the
+ * end of the region is padded with 0xff.
+ *
+ * NOTE: The current code assumes all parts are contiguous. The current
+ * update map fits in one eraseblock, but if it ever needs extending beyond
+ * that, better implement a bitmap to indicate which blocks need updating
+ * and which can be skipped. We don't want to erase and reprogram blocks
+ * for no good reason, it's bad for the flash lifespan.
+ */
+static const struct update_part updates[] = {
+ {
+ .filename = "spl." BOOTFILE_EXT,
+ .offset = 0,
+ .length = 12 * 1024,
+ },
+ {
+ .filename = "bootloader.ucl",
+ .offset = 0x6800,
+ .length = 102 * 1024,
+ },
+};
+
+static const int num_updates = sizeof(updates) / sizeof(struct update_part);
+
+static const uint8_t flash_sig_magic[8] =
+ {0x06, 0x05, 0x04, 0x03, 0x02, 0x55, 0xaa, 0x55};
+
+/* calculate the offset and length of the update image; this is constant
+ * for a given target, based on the update parts and the NAND chip geometry.
+ */
+static void get_image_loc(struct nand_drv* ndrv, size_t* offptr, size_t* lenptr)
+{
+ size_t blk_size = ndrv->chip->page_size << ndrv->chip->log2_ppb;
+ size_t img_off = 0;
+ size_t img_len = 0;
+
+ /* calculate minimal image needed to contain all update blocks */
+ for(int i = 0; i < num_updates; ++i) {
+ img_len = MAX(img_len, updates[i].offset + updates[i].length);
+ img_off = MIN(img_off, updates[i].offset);
+ }
+
+ /* round everything to a multiple of the block size */
+ size_t r_off = blk_size * (img_off / blk_size);
+ size_t r_len = blk_size * ((img_len + img_off - r_off + blk_size - 1) / blk_size);
+
+ *offptr = r_off;
+ *lenptr = r_len;
+}
+
+/* Read in a single part of the update from the tarball, and patch it
+ * into the image */
+static int patch_part(mtar_t* tar, const struct update_part* part,
+ uint8_t* img_buf, size_t img_off)
+{
+ int rc = mtar_find(tar, part->filename);
+ if(rc != MTAR_ESUCCESS)
+ return IERR_BAD_FORMAT;
+
+ const mtar_header_t* h = mtar_get_header(tar);
+ if(h->type != 0 && h->type != MTAR_TREG)
+ return IERR_BAD_FORMAT;
+
+ if(h->size > part->length)
+ return IERR_BAD_FORMAT;
+
+ /* wipe the patched area, and read in the new data */
+ memset(&img_buf[part->offset - img_off], 0xff, part->length);
+ rc = mtar_read_data(tar, &img_buf[part->offset - img_off], h->size);
+ if(rc < 0 || (unsigned)rc != h->size)
+ return IERR_FILE_IO;
+
+ return IERR_SUCCESS;
+}
+
+struct updater {
+ int buf_hnd; /* core_alloc handle for our memory buffer */
+ size_t buf_len; /* sizeof the buffer */
+
+ uint8_t* img_buf;
+ size_t img_off; /* image address in flash */
+ size_t img_len; /* image length in flash = size of the buffer */
+
+ mtar_t* tar;
+ struct nand_drv* ndrv;
+};
+
+static int updater_init(struct updater* u)
+{
+ int rc;
+
+ /* initialize stuff correctly */
+ u->buf_hnd = -1;
+ u->buf_len = 0;
+ u->img_buf = NULL;
+ u->img_off = 0;
+ u->img_len = 0;
+ u->tar = NULL;
+ u->ndrv = NULL;
+
+ /* open NAND */
+ u->ndrv = nand_init();
+ nand_lock(u->ndrv);
+ rc = nand_open(u->ndrv);
+ if(rc != NAND_SUCCESS) {
+ rc = IERR_NAND_OPEN;
+ goto error;
+ }
+
+ get_image_loc(u->ndrv, &u->img_off, &u->img_len);
+
+ /* buf_len is a bit oversized here, but it's not really important */
+ u->buf_len = u->img_len + sizeof(mtar_t) + 2*CACHEALIGN_SIZE;
+ u->buf_hnd = core_alloc_ex(u->buf_len, &buflib_ops_locked);
+ if(u->buf_hnd < 0) {
+ rc = IERR_OUT_OF_MEMORY;
+ goto error;
+ }
+
+ /* allocate from the buffer */
+ uint8_t* buffer = (uint8_t*)core_get_data(u->buf_hnd);
+ size_t buf_len = u->buf_len;
+
+ CACHEALIGN_BUFFER(buffer, buf_len);
+ u->img_buf = buffer;
+ buffer += u->img_len;
+ buf_len -= u->img_len;
+
+ CACHEALIGN_BUFFER(buffer, buf_len);
+ u->tar = (mtar_t*)buffer;
+ memset(u->tar, 0, sizeof(mtar_t));
+
+ rc = IERR_SUCCESS;
+
+ error:
+ return rc;
+}
+
+static void updater_cleanup(struct updater* u)
+{
+ if(u->tar && mtar_is_open(u->tar))
+ mtar_close(u->tar);
+
+ core_free(u->buf_hnd);
+
+ if(u->ndrv) {
+ nand_close(u->ndrv);
+ nand_unlock(u->ndrv);
+ }
+}
+
+int install_bootloader(const char* filename)
+{
+ struct updater u;
+ int rc = updater_init(&u);
+ if(rc != IERR_SUCCESS)
+ goto error;
+
+ /* get the image */
+ rc = nand_read_bytes(u.ndrv, u.img_off, u.img_len, u.img_buf);
+ if(rc != NAND_SUCCESS) {
+ rc = IERR_NAND_READ;
+ goto error;
+ }
+
+ /* get the tarball */
+ rc = mtar_open(u.tar, filename, O_RDONLY);
+ if(rc != MTAR_ESUCCESS) {
+ if(rc == MTAR_EOPENFAIL)
+ rc = IERR_FILE_NOT_FOUND;
+ else if(rc == MTAR_EREADFAIL)
+ rc = IERR_FILE_IO;
+ else
+ rc = IERR_BAD_FORMAT;
+ goto error;
+ }
+
+ /* patch stuff */
+ for(int i = 0; i < num_updates; ++i) {
+ rc = patch_part(u.tar, &updates[i], u.img_buf, u.img_off);
+ if(rc != IERR_SUCCESS)
+ goto error;
+ }
+
+ /* write back the patched image */
+ rc = nand_write_bytes(u.ndrv, u.img_off, u.img_len, u.img_buf);
+ if(rc != NAND_SUCCESS) {
+ rc = IERR_NAND_WRITE;
+ goto error;
+ }
+
+ rc = IERR_SUCCESS;
+
+ error:
+ updater_cleanup(&u);
+ return rc;
+}
+
+int backup_bootloader(const char* filename)
+{
+ int rc, fd = 0;
+ struct updater u;
+
+ rc = updater_init(&u);
+ if(rc != IERR_SUCCESS)
+ goto error;
+
+ /* read image */
+ rc = nand_read_bytes(u.ndrv, u.img_off, u.img_len, u.img_buf);
+ if(rc != NAND_SUCCESS) {
+ rc = IERR_NAND_READ;
+ goto error;
+ }
+
+ /* bail if we're backing up something that looks like garbage */
+ if (memcmp(u.img_buf, flash_sig_magic, 8)) {
+ rc = IERR_CORRUPTED_BACKUP;
+ goto error;
+ }
+
+ /* write to file */
+ fd = open(filename, O_CREAT|O_TRUNC|O_WRONLY);
+ if(fd < 0) {
+ rc = IERR_FILE_IO;
+ goto error;
+ }
+
+ ssize_t cnt = write(fd, u.img_buf, u.img_len);
+ if(cnt < 0 || (size_t)cnt != u.img_len) {
+ rc = IERR_FILE_IO;
+ goto error;
+ }
+
+ rc = IERR_SUCCESS;
+
+ error:
+ if(fd >= 0)
+ close(fd);
+ updater_cleanup(&u);
+ return rc;
+}
+
+int restore_bootloader(const char* filename)
+{
+ int rc, fd = 0;
+ struct updater u;
+
+ rc = updater_init(&u);
+ if(rc != IERR_SUCCESS)
+ goto error;
+
+ /* read from file */
+ fd = open(filename, O_RDONLY);
+ if(fd < 0) {
+ rc = IERR_FILE_NOT_FOUND;
+ goto error;
+ }
+
+ ssize_t cnt = read(fd, u.img_buf, u.img_len);
+ if(cnt < 0 || (size_t)cnt != u.img_len) {
+ rc = IERR_FILE_IO;
+ goto error;
+ }
+
+ /* safety check to reduce risk of flashing complete garbage */
+ if (memcmp(u.img_buf, flash_sig_magic, 8)) {
+ rc = IERR_CORRUPTED_BACKUP;
+ goto error;
+ }
+
+ /* write image */
+ rc = nand_write_bytes(u.ndrv, u.img_off, u.img_len, u.img_buf);
+ if(rc != NAND_SUCCESS) {
+ rc = IERR_NAND_WRITE;
+ goto error;
+ }
+
+ rc = IERR_SUCCESS;
+
+ error:
+ if(fd >= 0)
+ close(fd);
+ updater_cleanup(&u);
+ return rc;
+}
+
+const char* installer_strerror(int rc)
+{
+ switch(rc) {
+ case IERR_SUCCESS: return "Success";
+ case IERR_OUT_OF_MEMORY: return "Out of memory";
+ case IERR_FILE_NOT_FOUND: return "File not found";
+ case IERR_FILE_IO: return "Disk I/O error";
+ case IERR_BAD_FORMAT: return "Bad archive";
+ case IERR_NAND_OPEN: return "NAND open error";
+ case IERR_NAND_READ: return "NAND read error";
+ case IERR_NAND_WRITE: return "NAND write error";
+ case IERR_CORRUPTED_BACKUP: return "Backup is corrupt";
+ default: return "Unknown error!?";
+ }
+}
diff --git a/firmware/target/mips/ingenic_x1000/installer-x1000.h b/firmware/target/mips/ingenic_x1000/installer-x1000.h
new file mode 100644
index 0000000000..9b0f1e4bd6
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/installer-x1000.h
@@ -0,0 +1,56 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef __INSTALLER_X1000_H__
+#define __INSTALLER_X1000_H__
+
+/* This API is for the bootloader recovery menu and Rockbox utility to handle
+ * bootloader installation, backup, and restore.
+ *
+ * Currently the installer can only handle NAND flash, although the X1000 can
+ * boot from NOR flash or SD/MMC. Support for other storage media can be added
+ * when there is a target that needs it.
+ *
+ * Bootloader updates are tarballs, and they can "monkey patch" the flash in
+ * a customizable way (but fixed at compile time).
+ *
+ * Backup and restore simply takes the range of eraseblocks touched by the
+ * monkey patch and copies them to or from a regular file.
+ */
+
+enum {
+ IERR_SUCCESS = 0,
+ IERR_OUT_OF_MEMORY,
+ IERR_FILE_NOT_FOUND,
+ IERR_FILE_IO,
+ IERR_BAD_FORMAT,
+ IERR_NAND_OPEN,
+ IERR_NAND_READ,
+ IERR_NAND_WRITE,
+ IERR_CORRUPTED_BACKUP,
+};
+
+extern int install_bootloader(const char* filename);
+extern int backup_bootloader(const char* filename);
+extern int restore_bootloader(const char* filename);
+extern const char* installer_strerror(int rc);
+
+#endif /* __INSTALLER_X1000_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/kernel-x1000.c b/firmware/target/mips/ingenic_x1000/kernel-x1000.c
index c8105a3446..8d272bdaa9 100644
--- a/firmware/target/mips/ingenic_x1000/kernel-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/kernel-x1000.c
@@ -23,26 +23,6 @@
#include "system.h"
#include "x1000/ost.h"
-/* TODO: implement a CPU frequency switching policy based on CPU utilization
- *
- * The basic assumption is that the workload consumes a fixed number of CPU
- * cycles per second on average (= utilization), so we can set the frequency
- * based on that value. Audio playback should fit this usage pattern well, so
- * it's a good fit for Rockbox.
- *
- * It's easier to understand in terms of fluid flow -- we need to keep
- * a reservoir of water topped up, CPU frequency is the inflow rate, and
- * CPU utilization is the outflow rate. The goal is to avoid running dry
- * and minimize the inflow rate.
- *
- * The only tricky part here is handing usage spikes -- CPU frequency has to
- * increase faster than utilization or there's a risk of audio dropouts.
- *
- * Rockbox CPU boost could be used as a hint to scale up frequency faster.
- * If that's not necessary to get good results, HAVE_ADJUSTABLE_CPU_FREQ can
- * be disabled entirely.
- */
-
#define CPU_IDLE_SAMPLES 100
void tick_start(unsigned interval_in_ms)
@@ -57,6 +37,7 @@ void tick_start(unsigned interval_in_ms)
void OST(void)
{
+#ifdef X1000_CPUIDLE_STATS
/* CPU idle time accounting */
uint32_t now = __ost_read32();
uint32_t div = now - __cpu_idle_reftick;
@@ -67,6 +48,7 @@ void OST(void)
__cpu_idle_ticks = 0;
__cpu_idle_reftick = now;
}
+#endif
/* Call regular kernel tick */
jz_write(OST_1FLG, 0);
diff --git a/firmware/target/mips/ingenic_x1000/lcd-x1000.c b/firmware/target/mips/ingenic_x1000/lcd-x1000.c
index aadf93c8ff..979febf066 100644
--- a/firmware/target/mips/ingenic_x1000/lcd-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/lcd-x1000.c
@@ -65,8 +65,10 @@ static fb_data shadowfb[LCD_HEIGHT*LCD_WIDTH] __attribute__((aligned(64)));
/* Signals DMA copy to shadow FB is done */
static volatile int fbcopy_done;
+#if defined(HAVE_LCD_SLEEP) || defined(LCD_X1000_FASTSLEEP)
/* True if we're in sleep mode */
static bool lcd_sleeping = false;
+#endif
/* Check if running with interrupts disabled (eg: panic screen) */
#define lcd_panic_mode \
@@ -98,16 +100,16 @@ static void lcd_init_controller(const struct lcd_tgt_config* cfg)
default: break;
}
- if(lcd_tgt_config.use_serial)
+ if(cfg->use_serial)
mcfg_new |= jz_orf(LCD_MCFG_NEW, DTYPE_V(SERIAL), CTYPE_V(SERIAL));
else
mcfg_new |= jz_orf(LCD_MCFG_NEW, DTYPE_V(PARALLEL), CTYPE_V(PARALLEL));
jz_vwritef(mcfg_new, LCD_MCFG_NEW,
- 6800_MODE(lcd_tgt_config.use_6800_mode),
- CSPLY(lcd_tgt_config.wr_polarity ? 0 : 1),
- RSPLY(lcd_tgt_config.dc_polarity),
- CLKPLY(lcd_tgt_config.clk_polarity));
+ 6800_MODE(cfg->use_6800_mode),
+ CSPLY(cfg->wr_polarity ? 0 : 1),
+ RSPLY(cfg->dc_polarity),
+ CLKPLY(cfg->clk_polarity));
/* Program the configuration. Note we cannot enable TE signal at
* this stage, because the panel will need to be configured first.
@@ -122,9 +124,9 @@ static void lcd_init_controller(const struct lcd_tgt_config* cfg)
jz_write(LCD_SMWT, 0);
/* DMA settings */
- jz_writef(LCD_CTRL, BURST_V(64WORD),
+ jz_writef(LCD_CTRL, ENABLE(0), BURST_V(64WORD),
EOFM(1), SOFM(0), IFUM(0), QDM(0),
- BEDN(0), PEDN(0), ENABLE(0));
+ BEDN(cfg->big_endian), PEDN(0));
jz_write(LCD_DAH, LCD_WIDTH);
jz_write(LCD_DAV, LCD_HEIGHT);
}
@@ -274,8 +276,10 @@ static void lcd_fbcopy_dma_partial(int x, int y, int width, int height)
static void lcd_dma_start(void)
{
- /* Set format conversion bit, seems necessary for DMA mode */
- jz_writef(LCD_MCFG_NEW, FMT_CONV(1));
+ /* Set format conversion bit, seems necessary for DMA mode.
+ * Must set DTIMES here if we use an 8-bit bus type. */
+ int dtimes = lcd_tgt_config.bus_width == 8 ? (LCD_DEPTH/8 - 1) : 0;
+ jz_writef(LCD_MCFG_NEW, FMT_CONV(1), DTIMES(dtimes));
/* Program vsync configuration */
jz_writef(LCD_MCTRL, NARROW_TE(lcd_tgt_config.te_narrow),
@@ -290,21 +294,6 @@ static void lcd_dma_start(void)
jz_writef(LCD_CTRL, ENABLE(1));
}
-static void lcd_dma_stop(void)
-{
- /* Stop the DMA transfer */
- jz_writef(LCD_CTRL, ENABLE(0));
- jz_writef(LCD_MCTRL, DMA_TX_EN(0));
-
- /* Wait for disable to take effect */
- while(jz_readf(LCD_STATE, QD) == 0);
- jz_writef(LCD_STATE, QD(0));
-
- /* Clear format conversion bit, disable vsync */
- jz_writef(LCD_MCFG_NEW, FMT_CONV(0));
- jz_writef(LCD_MCTRL, NARROW_TE(0), TE_INV(0), NOT_USE_TE(1));
-}
-
static bool lcd_wait_frame(void)
{
/* Bail out if DMA is not enabled */
@@ -321,6 +310,26 @@ static bool lcd_wait_frame(void)
return true;
}
+static void lcd_dma_stop(void)
+{
+#ifdef LCD_X1000_DMA_WAIT_FOR_FRAME
+ /* Wait for frame to finish to avoid misaligning the write pointer */
+ lcd_wait_frame();
+#endif
+
+ /* Stop the DMA transfer */
+ jz_writef(LCD_CTRL, ENABLE(0));
+ jz_writef(LCD_MCTRL, DMA_TX_EN(0));
+
+ /* Wait for disable to take effect */
+ while(jz_readf(LCD_STATE, QD) == 0);
+ jz_writef(LCD_STATE, QD(0));
+
+ /* Clear format conversion bit, disable vsync */
+ jz_writef(LCD_MCFG_NEW, FMT_CONV(0), DTIMES(0));
+ jz_writef(LCD_MCTRL, NARROW_TE(0), TE_INV(0), NOT_USE_TE(1));
+}
+
static void lcd_send(uint32_t d)
{
while(jz_readf(LCD_MSTATE, BUSY));
@@ -404,7 +413,8 @@ void lcd_enable(bool en)
restore_irq(irq);
/* Deal with sleep mode */
-#ifdef LCD_X1000_FASTSLEEP
+#if defined(HAVE_LCD_SLEEP) || defined(LCD_X1000_FASTSLEEP)
+#if defined(LCD_X1000_FASTSLEEP)
if(bit && !en) {
lcd_tgt_sleep(true);
lcd_sleeping = true;
@@ -414,10 +424,14 @@ void lcd_enable(bool en)
lcd_tgt_sleep(false);
lcd_sleeping = false;
}
+#endif
/* Handle turning the LCD back on */
if(!bit && en)
+ {
+ send_event(LCD_EVENT_ACTIVATION, NULL);
lcd_dma_start();
+ }
}
#endif
@@ -446,6 +460,10 @@ void lcd_update(void)
jz_writef(LCD_MCTRL, DMA_START(1), DMA_MODE(1));
}
+/* We can do partial updates even though the DMA doesn't seem to handle it well,
+ * due to the fact that this is actually putting it into a buffer, and then
+ * it gets transferred via DMA to a secondary buffer, which gets transferred in
+ * its entirety to the LCD through a different DMA process. */
void lcd_update_rect(int x, int y, int width, int height)
{
/* Clamp the coordinates */
diff --git a/firmware/target/mips/ingenic_x1000/lcd-x1000.h b/firmware/target/mips/ingenic_x1000/lcd-x1000.h
index 96085ac207..e88a8733ed 100644
--- a/firmware/target/mips/ingenic_x1000/lcd-x1000.h
+++ b/firmware/target/mips/ingenic_x1000/lcd-x1000.h
@@ -38,34 +38,37 @@
struct lcd_tgt_config {
/* Data bus width, in bits */
- int bus_width: 8;
+ unsigned bus_width: 8;
/* Command bus width, in bits */
- int cmd_width: 8;
+ unsigned cmd_width: 8;
/* 1 = use 6800 timings, 0 = use 8080 timings */
- int use_6800_mode: 1;
+ unsigned use_6800_mode: 1;
/* 1 = serial interface, 0 = parallel interface */
- int use_serial: 1;
+ unsigned use_serial: 1;
/* Clock active edge: 0 = falling edge, 1 = rising edge */
- int clk_polarity: 1;
+ unsigned clk_polarity: 1;
/* DC pin levels: 1 = data high, command low; 0 = data low, command high */
- int dc_polarity: 1;
+ unsigned dc_polarity: 1;
/* WR pin level during idle: 1 = keep high; 0 = keep low */
- int wr_polarity: 1;
+ unsigned wr_polarity: 1;
/* 1 to enable vsync, so DMA transfer is synchronized with TE signal */
- int te_enable: 1;
+ unsigned te_enable: 1;
/* Active level of TE signal: 1 = high, 0 = low */
- int te_polarity: 1;
+ unsigned te_polarity: 1;
/* 1 = support narrow TE signal (<=3 pixel clocks); 0 = don't support */
- int te_narrow: 1;
+ unsigned te_narrow: 1;
+
+ /* 1 = big endian mode, 0 = little endian mode */
+ unsigned big_endian: 1;
/* Commands used to initiate a framebuffer write. Buffer must be
* aligned to 64-byte boundary and size must be a multiple of 4,
@@ -102,6 +105,14 @@ extern void lcd_exec_commands(const uint32_t* cmdseq);
*/
extern void lcd_tgt_enable(bool on);
+/* Enable/disable the LCD controller, but intended for booting the OF.
+ *
+ * This is only used for the Eros Q Native port, as the OF seems to be
+ * unable to initialize the LCD in the kernel boot rather than having
+ * the bootloader do it.
+ */
+extern void lcd_tgt_enable_of(bool on);
+
/* Enter or exit sleep mode to save power, normally by sending the necessary
* commands with lcd_exec_commands().
*/
diff --git a/firmware/target/mips/ingenic_x1000/msc-x1000.c b/firmware/target/mips/ingenic_x1000/msc-x1000.c
index 62aa76988c..62172fa213 100644
--- a/firmware/target/mips/ingenic_x1000/msc-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/msc-x1000.c
@@ -21,6 +21,7 @@
#include "system.h"
#include "panic.h"
+#include "led.h"
#include "msc-x1000.h"
#include "gpio-x1000.h"
#include "irq-x1000.h"
@@ -39,16 +40,39 @@
* ensure that removing the card always resets the driver to a sane state.
*/
+#define DEBOUNCE_TIME (HZ/10)
+
static const msc_config msc_configs[] = {
-#ifdef FIIO_M3K
+#if defined(FIIO_M3K)
+#define MSC_CLOCK_SOURCE X1000_CLK_SCLK_A
+ {
+ .msc_nr = 0,
+ .msc_type = MSC_TYPE_SD,
+ .bus_width = 4,
+ .label = "microSD",
+ .cd_gpio = GPIO_MSC0_CD,
+ .cd_active_level = 0,
+ },
+#elif defined(SHANLING_Q1)
+#define MSC_CLOCK_SOURCE X1000_CLK_MPLL
+ {
+ .msc_nr = 0,
+ .msc_type = MSC_TYPE_SD,
+ .bus_width = 4,
+ .label = "microSD",
+ .cd_gpio = GPIO_MSC0_CD,
+ .cd_active_level = 0,
+ },
+ /* NOTE: SDIO wifi card is on msc1 */
+#elif defined(EROS_QN)
#define MSC_CLOCK_SOURCE X1000_CLK_SCLK_A
-#define msc0_cd_interrupt GPIOB06
{
.msc_nr = 0,
.msc_type = MSC_TYPE_SD,
.bus_width = 4,
.label = "microSD",
- .cd_gpio = {GPIO_B, 1 << 6, 0},
+ .cd_gpio = GPIO_MSC0_CD,
+ .cd_active_level = 0,
},
#else
# error "Please add X1000 MSC config"
@@ -66,6 +90,9 @@ static const msc_config* msc_lookup_config(int msc)
static msc_drv msc_drivers[MSC_COUNT];
+static void msc0_cd_interrupt(void);
+static void msc1_cd_interrupt(void);
+
/* ---------------------------------------------------------------------------
* Initialization
*/
@@ -102,6 +129,7 @@ static void msc_init_one(msc_drv* d, int msc)
d->req = NULL;
d->iflag_done = 0;
d->card_present = 1;
+ d->card_present_last = 1;
d->req_running = 0;
mutex_init(&d->lock);
semaphore_init(&d->cmd_done, 1, 0);
@@ -117,40 +145,18 @@ static void msc_init_one(msc_drv* d, int msc)
msc_full_reset(d);
system_enable_irq(msc == 0 ? IRQ_MSC0 : IRQ_MSC1);
- /* Configure bus pins */
- int port, device;
- unsigned pins;
- if(msc == 0) {
- port = GPIO_A;
- device = 1;
- switch(d->config->bus_width) {
- case 8: pins = 0x3ff << 16; break;
- case 4: pins = 0x03f << 20; break;
- case 1: pins = 0x007 << 23; break;
- default: pins = 0; break;
- }
- } else {
- port = GPIO_C;
- device = 0;
- switch(d->config->bus_width) {
- case 4: pins = 0x3f; break;
- case 1: pins = 0x07; break;
- default: pins = 0; break;
- }
- }
-
- gpio_config(port, pins, GPIO_DEVICE(device));
-
/* Setup the card detect IRQ */
- if(d->config->cd_gpio.pin) {
- port = d->config->cd_gpio.port;
- pins = d->config->cd_gpio.pin;
- int level = (REG_GPIO_PIN(port) & pins) ? 1 : 0;
- if(level != d->config->cd_gpio.active_level)
+ if(d->config->cd_gpio != GPIO_NONE) {
+ if(gpio_get_level(d->config->cd_gpio) != d->config->cd_active_level) {
d->card_present = 0;
+ d->card_present_last = 0;
+ }
- gpio_config(port, pins, GPIO_IRQ_EDGE(level ? 0 : 1));
- gpio_enable_irq(port, pins);
+ system_set_irq_handler(GPIO_TO_IRQ(d->config->cd_gpio),
+ msc == 0 ? msc0_cd_interrupt : msc1_cd_interrupt);
+ gpio_set_function(d->config->cd_gpio, GPIOF_IRQ_EDGE(1));
+ gpio_flip_edge_irq(d->config->cd_gpio);
+ gpio_enable_irq(d->config->cd_gpio);
}
}
@@ -212,12 +218,20 @@ void msc_full_reset(msc_drv* d)
bool msc_card_detect(msc_drv* d)
{
- if(!d->config->cd_gpio.pin)
+ if(d->config->cd_gpio == GPIO_NONE)
return true;
- int l = REG_GPIO_PIN(d->config->cd_gpio.port) & d->config->cd_gpio.pin;
- l = l ? 1 : 0;
- return l == d->config->cd_gpio.active_level;
+ return gpio_get_level(d->config->cd_gpio) == d->config->cd_active_level;
+}
+
+void msc_led_trigger(void)
+{
+ bool state = false;
+ for(int i = 0; i < MSC_COUNT; ++i)
+ if(msc_drivers[i].req_running)
+ state = true;
+
+ led(state);
}
/* ---------------------------------------------------------------------------
@@ -335,10 +349,12 @@ void msc_set_speed(msc_drv* d, int rate)
jz_writef(MSC_LPM(d->msc_nr),
DRV_SEL_V(RISE_EDGE_DELAY_QTR_PHASE),
SMP_SEL_V(RISE_EDGE_DELAYED));
+ jz_writef(MSC_CTRL2(d->msc_nr), SPEED_V(HIGHSPEED));
} else {
jz_writef(MSC_LPM(d->msc_nr),
DRV_SEL_V(FALL_EDGE),
SMP_SEL_V(RISE_EDGE));
+ jz_writef(MSC_CTRL2(d->msc_nr), SPEED_V(DEFAULT));
}
/* Restart clock if it was running before */
@@ -372,6 +388,8 @@ static void msc_finish_request(msc_drv* d, int status)
d->req->status = status;
d->req_running = 0;
d->iflag_done = 0;
+
+ msc_led_trigger();
timeout_cancel(&d->cmd_tmo);
semaphore_release(&d->cmd_done);
}
@@ -443,16 +461,23 @@ void msc_async_start(msc_drv* d, msc_req* r)
else
discard_dcache_range(r->data, d->dma_desc.len);
- /* TODO - should use MODE_SEL bit? what value of INCR? */
- unsigned long addr_off = ((unsigned long)r->data) & 3;
- jz_writef(MSC_DMAC(d->msc_nr), MODE_SEL(0), INCR(0), DMASEL(0),
- ALIGN_EN(addr_off != 0 ? 1 : 0), ADDR_OFFSET(addr_off));
+ /* Unaligned address for DMA doesn't seem to work correctly.
+ * FAT FS driver should ensure proper alignment of all buffers,
+ * so in practice this panic should not occur, but if it does
+ * I want to hear about it. */
+ if(UNLIKELY(d->dma_desc.mem & 3)) {
+ panicf("msc%d bad align: %08x", d->msc_nr,
+ (unsigned)d->dma_desc.mem);
+ }
+
+ jz_writef(MSC_DMAC(d->msc_nr), MODE_SEL(0), INCR(0), DMASEL(0));
REG_MSC_DMANDA(d->msc_nr) = PHYSADDR(&d->dma_desc);
}
/* Begin processing */
d->req = r;
d->req_running = 1;
+ msc_led_trigger();
jz_writef(MSC_CTRL(d->msc_nr), START_OP(1));
if(r->flags & MSC_RF_DATA)
jz_writef(MSC_DMAC(d->msc_nr), ENABLE(1));
@@ -628,11 +653,25 @@ static void msc_interrupt(msc_drv* d)
static int msc_cd_callback(struct timeout* tmo)
{
msc_drv* d = (msc_drv*)tmo->data;
+ int now_present = msc_card_detect(d) ? 1 : 0;
- /* If card is still present we assume the card is properly inserted */
- if(msc_card_detect(d)) {
- d->card_present = 1;
- queue_broadcast(SYS_HOTSWAP_INSERTED, d->drive_nr);
+ /* If the CD pin level changed during the timeout interval, then the
+ * signal is not yet stable and we need to wait longer. */
+ if(now_present != d->card_present_last) {
+ d->card_present_last = now_present;
+ return DEBOUNCE_TIME;
+ }
+
+ /* If there is a change, then broadcast the hotswap event */
+ if(now_present != d->card_present) {
+ if(now_present) {
+ d->card_present = 1;
+ queue_broadcast(SYS_HOTSWAP_INSERTED, d->drive_nr);
+ } else {
+ msc_async_abort(d, MSC_REQ_EXTRACTED);
+ d->card_present = 0;
+ queue_broadcast(SYS_HOTSWAP_EXTRACTED, d->drive_nr);
+ }
}
return 0;
@@ -640,20 +679,12 @@ static int msc_cd_callback(struct timeout* tmo)
static void msc_cd_interrupt(msc_drv* d)
{
- if(!msc_card_detect(d)) {
- /* Immediately abort and notify when removing a card */
- msc_async_abort(d, MSC_REQ_EXTRACTED);
- if(d->card_present) {
- d->card_present = 0;
- queue_broadcast(SYS_HOTSWAP_EXTRACTED, d->drive_nr);
- }
- } else {
- /* Timer to debounce input */
- timeout_register(&d->cd_tmo, msc_cd_callback, HZ/4, (intptr_t)d);
- }
+ /* Timer to debounce input */
+ d->card_present_last = msc_card_detect(d) ? 1 : 0;
+ timeout_register(&d->cd_tmo, msc_cd_callback, DEBOUNCE_TIME, (intptr_t)d);
/* Invert the IRQ */
- REG_GPIO_PAT0(d->config->cd_gpio.port) ^= d->config->cd_gpio.pin;
+ gpio_flip_edge_irq(d->config->cd_gpio);
}
void MSC0(void)
@@ -666,19 +697,15 @@ void MSC1(void)
msc_interrupt(&msc_drivers[1]);
}
-#ifdef msc0_cd_interrupt
-void msc0_cd_interrupt(void)
+static void msc0_cd_interrupt(void)
{
msc_cd_interrupt(&msc_drivers[0]);
}
-#endif
-#ifdef msc1_cd_interrupt
-void msc1_cd_interrupt(void)
+static void msc1_cd_interrupt(void)
{
msc_cd_interrupt(&msc_drivers[1]);
}
-#endif
/* ---------------------------------------------------------------------------
* SD command helpers
diff --git a/firmware/target/mips/ingenic_x1000/msc-x1000.h b/firmware/target/mips/ingenic_x1000/msc-x1000.h
index 53a5b301f0..3ebdd3adc3 100644
--- a/firmware/target/mips/ingenic_x1000/msc-x1000.h
+++ b/firmware/target/mips/ingenic_x1000/msc-x1000.h
@@ -83,18 +83,13 @@
#define MSC_SPEED_FAST 25000000
#define MSC_SPEED_HIGH 50000000
-typedef struct msc_gpio_data {
- int port;
- int pin;
- int active_level;
-} msc_gpio_data;
-
typedef struct msc_config {
int msc_nr;
int msc_type;
int bus_width;
const char* label;
- struct msc_gpio_data cd_gpio;
+ int cd_gpio;
+ int cd_active_level;
} msc_config;
typedef struct msc_req {
@@ -131,7 +126,8 @@ typedef struct msc_drv {
unsigned iflag_done;
volatile int req_running;
- volatile int card_present;
+ volatile int card_present; /* Debounced status */
+ volatile int card_present_last; /* Status when we last polled it */
struct mutex lock;
struct semaphore cmd_done;
@@ -152,6 +148,8 @@ extern void msc_unlock(msc_drv* d);
extern void msc_full_reset(msc_drv* d);
extern bool msc_card_detect(msc_drv* d);
+extern void msc_led_trigger(void);
+
/* Controller API */
extern void msc_ctl_reset(msc_drv* d);
extern void msc_set_clock_mode(msc_drv* d, int mode);
diff --git a/firmware/target/mips/ingenic_x1000/nand-x1000-err.h b/firmware/target/mips/ingenic_x1000/nand-x1000-err.h
deleted file mode 100644
index 252d591062..0000000000
--- a/firmware/target/mips/ingenic_x1000/nand-x1000-err.h
+++ /dev/null
@@ -1,18 +0,0 @@
-#ifndef __NAND_X1000_ERR_H__
-#define __NAND_X1000_ERR_H__
-
-/* Error codes which can be returned by the nand-x1000 API. These codes are
- * also used by host-side tools, so we define them here to avoid polluting
- * the namespace with useless X1000 APIs. */
-#define NANDERR_CHIP_UNSUPPORTED (-1)
-#define NANDERR_WRITE_PROTECTED (-2)
-#define NANDERR_UNALIGNED_ADDRESS (-3)
-#define NANDERR_UNALIGNED_LENGTH (-4)
-#define NANDERR_READ_FAILED (-5)
-#define NANDERR_ECC_FAILED (-6)
-#define NANDERR_ERASE_FAILED (-7)
-#define NANDERR_PROGRAM_FAILED (-8)
-#define NANDERR_COMMAND_FAILED (-9)
-#define NANDERR_OTHER (-99)
-
-#endif /* __NAND_X1000_ERR_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/nand-x1000.c b/firmware/target/mips/ingenic_x1000/nand-x1000.c
index e6d5b6e4c7..af0f972eae 100644
--- a/firmware/target/mips/ingenic_x1000/nand-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/nand-x1000.c
@@ -20,507 +20,449 @@
****************************************************************************/
#include "nand-x1000.h"
-#include "nand-target.h"
#include "sfc-x1000.h"
#include "system.h"
+#include "logf.h"
#include <string.h>
-#if !defined(NAND_MAX_PAGE_SIZE) || \
- !defined(NAND_INIT_SFC_DEV_CONF) || \
- !defined(NAND_INIT_CLOCK_SPEED)
-# error "Target needs to specify NAND driver parameters"
-#endif
-
-/* Must be at least as big as a cacheline */
-#define NAND_AUX_BUFFER_SIZE CACHEALIGN_SIZE
+static void winbond_setup_chip(struct nand_drv* drv);
+
+static const struct nand_chip chip_ato25d1ga = {
+ .log2_ppb = 6, /* 64 pages */
+ .page_size = 2048,
+ .oob_size = 64,
+ .nr_blocks = 1024,
+ .bbm_pos = 2048,
+ .clock_freq = 150000000,
+ .dev_conf = jz_orf(SFC_DEV_CONF,
+ CE_DL(1), HOLD_DL(1), WP_DL(1),
+ CPHA(0), CPOL(0),
+ TSH(7), TSETUP(0), THOLD(0),
+ STA_TYPE_V(1BYTE), CMD_TYPE_V(8BITS),
+ SMP_DELAY(1)),
+ .flags = NAND_CHIPFLAG_QUAD | NAND_CHIPFLAG_HAS_QE_BIT,
+ .cmd_page_read = NANDCMD_PAGE_READ,
+ .cmd_program_execute = NANDCMD_PROGRAM_EXECUTE,
+ .cmd_block_erase = NANDCMD_BLOCK_ERASE,
+ .cmd_read_cache = NANDCMD_READ_CACHE_x4,
+ .cmd_program_load = NANDCMD_PROGRAM_LOAD_x4,
+};
-/* Writes have been enabled */
-#define NAND_DRV_FLAG_WRITEABLE 0x01
+static const struct nand_chip chip_w25n01gvxx = {
+ .log2_ppb = 6, /* 64 pages */
+ .page_size = 2048,
+ .oob_size = 64,
+ .nr_blocks = 1024,
+ .bbm_pos = 2048,
+ .clock_freq = 150000000,
+ .dev_conf = jz_orf(SFC_DEV_CONF,
+ CE_DL(1), HOLD_DL(1), WP_DL(1),
+ CPHA(0), CPOL(0),
+ TSH(11), TSETUP(0), THOLD(0),
+ STA_TYPE_V(1BYTE), CMD_TYPE_V(8BITS),
+ SMP_DELAY(1)),
+ .flags = NAND_CHIPFLAG_ON_DIE_ECC,
+ /* TODO: quad mode? */
+ .cmd_page_read = NANDCMD_PAGE_READ,
+ .cmd_program_execute = NANDCMD_PROGRAM_EXECUTE,
+ .cmd_block_erase = NANDCMD_BLOCK_ERASE,
+ .cmd_read_cache = NANDCMD_READ_CACHE_SLOW,
+ .cmd_program_load = NANDCMD_PROGRAM_LOAD,
+ .setup_chip = winbond_setup_chip,
+};
-/* Defined by target */
-extern const nand_chip_desc target_nand_chip_descs[];
+static const struct nand_chip chip_gd5f1gq4xexx = {
+ .log2_ppb = 6, /* 64 pages */
+ .page_size = 2048,
+ .oob_size = 64, /* 128B when hardware ECC is disabled */
+ .nr_blocks = 1024,
+ .bbm_pos = 2048,
+ .clock_freq = 150000000,
+ .dev_conf = jz_orf(SFC_DEV_CONF,
+ CE_DL(1), HOLD_DL(1), WP_DL(1),
+ CPHA(0), CPOL(0),
+ TSH(7), TSETUP(0), THOLD(0),
+ STA_TYPE_V(1BYTE), CMD_TYPE_V(8BITS),
+ SMP_DELAY(1)),
+ .flags = NAND_CHIPFLAG_QUAD | NAND_CHIPFLAG_HAS_QE_BIT |
+ NAND_CHIPFLAG_ON_DIE_ECC,
+ .cmd_page_read = NANDCMD_PAGE_READ,
+ .cmd_program_execute = NANDCMD_PROGRAM_EXECUTE,
+ .cmd_block_erase = NANDCMD_BLOCK_ERASE,
+ .cmd_read_cache = NANDCMD_READ_CACHE_x4,
+ .cmd_program_load = NANDCMD_PROGRAM_LOAD_x4,
+};
-#ifdef BOOTLOADER_SPL
-# define NANDBUFFER_ATTR __attribute__((section(".sdram"))) CACHEALIGN_ATTR
-#else
-# define NANDBUFFER_ATTR CACHEALIGN_ATTR
-#endif
+#define chip_ds35x1gaxxx chip_gd5f1gq4xexx
+#define chip_gd5f1gq5xexxg chip_gd5f1gq4xexx
+
+const struct nand_chip_id supported_nand_chips[] = {
+ NAND_CHIP_ID(&chip_ato25d1ga, NAND_READID_ADDR, 0x9b, 0x12),
+ NAND_CHIP_ID(&chip_w25n01gvxx, NAND_READID_ADDR, 0xef, 0xaa, 0x21),
+ NAND_CHIP_ID(&chip_gd5f1gq4xexx, NAND_READID_ADDR, 0xc8, 0xd1),
+ NAND_CHIP_ID(&chip_gd5f1gq4xexx, NAND_READID_ADDR, 0xc8, 0xc1),
+ NAND_CHIP_ID(&chip_ds35x1gaxxx, NAND_READID_ADDR, 0xe5, 0x71), /* 3.3 V */
+ NAND_CHIP_ID(&chip_ds35x1gaxxx, NAND_READID_ADDR, 0xe5, 0x21), /* 1.8 V */
+ NAND_CHIP_ID(&chip_gd5f1gq5xexxg, NAND_READID_ADDR, 0xc8, 0x51), /* 3.3 V */
+ NAND_CHIP_ID(&chip_gd5f1gq5xexxg, NAND_READID_ADDR, 0xc8, 0x41), /* 1.8 V */
+};
-/* Globals for the driver */
-static unsigned char pagebuffer[NAND_MAX_PAGE_SIZE] NANDBUFFER_ATTR;
-static unsigned char auxbuffer[NAND_AUX_BUFFER_SIZE] NANDBUFFER_ATTR;
-static nand_drv nand_driver;
+const size_t nr_supported_nand_chips = ARRAYLEN(supported_nand_chips);
-static void nand_drv_reset(nand_drv* d)
-{
- d->chip_ops = NULL;
- d->chip_data = NULL;
- d->pagebuf = &pagebuffer[0];
- d->auxbuf = &auxbuffer[0];
- d->raw_page_size = 0;
- d->flags = 0;
-}
+static struct nand_drv static_nand_drv;
+static uint8_t static_scratch_buf[NAND_DRV_SCRATCHSIZE] CACHEALIGN_ATTR;
+static uint8_t static_page_buf[NAND_DRV_MAXPAGESIZE] CACHEALIGN_ATTR;
-/* Driver code */
-int nand_open(void)
+struct nand_drv* nand_init(void)
{
- sfc_init();
- sfc_lock();
-
- /* Reset driver state */
- nand_drv_reset(&nand_driver);
-
- /* Init hardware */
- sfc_open();
- sfc_set_dev_conf(NAND_INIT_SFC_DEV_CONF);
- sfc_set_clock(NAND_CLOCK_SOURCE, NAND_INIT_CLOCK_SPEED);
-
- /* Identify NAND chip */
- int status = 0;
- int nandid = nandcmd_read_id(&nand_driver);
- if(nandid < 0) {
- status = NANDERR_CHIP_UNSUPPORTED;
- goto _err;
+ static bool inited = false;
+ if(!inited) {
+ mutex_init(&static_nand_drv.mutex);
+ static_nand_drv.scratch_buf = static_scratch_buf;
+ static_nand_drv.page_buf = static_page_buf;
+ static_nand_drv.refcount = 0;
}
- unsigned char mf_id = nandid >> 8;
- unsigned char dev_id = nandid & 0xff;
- const nand_chip_desc* desc = &target_nand_chip_descs[0];
- while(1) {
- if(desc->data == NULL || desc->ops == NULL) {
- status = NANDERR_CHIP_UNSUPPORTED;
- goto _err;
- }
-
- if(desc->data->mf_id == mf_id && desc->data->dev_id == dev_id)
- break;
- }
-
- /* Fill driver parameters */
- nand_driver.chip_ops = desc->ops;
- nand_driver.chip_data = desc->data;
- nand_driver.raw_page_size = desc->data->page_size + desc->data->spare_size;
-
- /* Configure hardware and run init op */
- sfc_set_dev_conf(desc->data->dev_conf);
- sfc_set_clock(NAND_CLOCK_SOURCE, desc->data->clock_freq);
+ return &static_nand_drv;
+}
- if((status = desc->ops->open(&nand_driver)) < 0)
- goto _err;
+static uint8_t nand_get_reg(struct nand_drv* drv, uint8_t reg)
+{
+ sfc_exec(NANDCMD_GET_FEATURE, reg, drv->scratch_buf, 1|SFC_READ);
+ return drv->scratch_buf[0];
+}
- _exit:
- sfc_unlock();
- return status;
- _err:
- nand_drv_reset(&nand_driver);
- sfc_close();
- goto _exit;
+static void nand_set_reg(struct nand_drv* drv, uint8_t reg, uint8_t val)
+{
+ drv->scratch_buf[0] = val;
+ sfc_exec(NANDCMD_SET_FEATURE, reg, drv->scratch_buf, 1|SFC_WRITE);
}
-void nand_close(void)
+static void nand_upd_reg(struct nand_drv* drv, uint8_t reg, uint8_t msk, uint8_t val)
{
- sfc_lock();
- nand_driver.chip_ops->close(&nand_driver);
- nand_drv_reset(&nand_driver);
- sfc_close();
- sfc_unlock();
+ uint8_t x = nand_get_reg(drv, reg);
+ x &= ~msk;
+ x |= val;
+ nand_set_reg(drv, reg, x);
}
-int nand_enable_writes(bool en)
+static const struct nand_chip* identify_chip_method(uint8_t method,
+ const uint8_t* id_buf)
{
- sfc_lock();
-
- int st = nand_driver.chip_ops->set_wp_enable(&nand_driver, en);
- if(st >= 0) {
- if(en)
- nand_driver.flags |= NAND_DRV_FLAG_WRITEABLE;
- else
- nand_driver.flags &= ~NAND_DRV_FLAG_WRITEABLE;
+ for (size_t i = 0; i < nr_supported_nand_chips; ++i) {
+ const struct nand_chip_id* chip_id = &supported_nand_chips[i];
+ if (chip_id->method == method &&
+ !memcmp(chip_id->id_bytes, id_buf, chip_id->num_id_bytes))
+ return chip_id->chip;
}
- sfc_unlock();
- return st;
+ return NULL;
}
-extern int nand_read_bytes(uint32_t byteaddr, int count, void* buf)
+static bool identify_chip(struct nand_drv* drv)
{
- if(count <= 0)
- return 0;
-
- nand_drv* d = &nand_driver;
- uint32_t rowaddr = byteaddr / d->chip_data->page_size;
- uint32_t coladdr = byteaddr % d->chip_data->page_size;
- unsigned char* dstbuf = (unsigned char*)buf;
- int status = 0;
-
- sfc_lock();
- do {
- if(d->chip_ops->read_page(d, rowaddr, d->pagebuf) < 0) {
- status = NANDERR_READ_FAILED;
- goto _end;
- }
-
- if(d->chip_ops->ecc_read(d, d->pagebuf) < 0) {
- status = NANDERR_ECC_FAILED;
- goto _end;
- }
-
- int amount = d->chip_data->page_size - coladdr;
- if(amount > count)
- amount = count;
-
- memcpy(dstbuf, d->pagebuf, amount);
- dstbuf += amount;
- count -= amount;
- rowaddr += 1;
- coladdr = 0;
- } while(count > 0);
-
- _end:
- sfc_unlock();
- return status;
+ /* Read ID command has some variations; Linux handles these 3:
+ * - no address or dummy bytes
+ * - 1 byte address, no dummy byte
+ * - no address byte, 1 byte dummy
+ *
+ * Currently we use the 2nd method, aka. address read ID, the
+ * other methods can be added when needed.
+ */
+ sfc_exec(NANDCMD_READID_ADDR, 0, drv->scratch_buf, 4|SFC_READ);
+ drv->chip = identify_chip_method(NAND_READID_ADDR, drv->scratch_buf);
+ if (drv->chip)
+ return true;
+
+ return false;
}
-int nand_write_bytes(uint32_t byteaddr, int count, const void* buf)
+static void setup_chip_data(struct nand_drv* drv)
{
- nand_drv* d = &nand_driver;
-
- if((d->flags & NAND_DRV_FLAG_WRITEABLE) == 0)
- return NANDERR_WRITE_PROTECTED;
-
- if(count <= 0)
- return 0;
-
- uint32_t rowaddr = byteaddr / d->chip_data->page_size;
- uint32_t coladdr = byteaddr % d->chip_data->page_size;
+ drv->ppb = 1 << drv->chip->log2_ppb;
+ drv->fpage_size = drv->chip->page_size + drv->chip->oob_size;
+}
- /* Only support whole page writes right now */
- if(coladdr != 0)
- return NANDERR_UNALIGNED_ADDRESS;
- if(count % d->chip_data->page_size)
- return NANDERR_UNALIGNED_LENGTH;
+static void winbond_setup_chip(struct nand_drv* drv)
+{
+ /* Ensure we are in buffered read mode. */
+ nand_upd_reg(drv, FREG_CFG, FREG_CFG_WINBOND_BUF, FREG_CFG_WINBOND_BUF);
+}
- const unsigned char* srcbuf = (const unsigned char*)buf;
- int status = 0;
+static void setup_chip_registers(struct nand_drv* drv)
+{
+ /* Set chip registers to enter normal operation */
+ if(drv->chip->flags & NAND_CHIPFLAG_HAS_QE_BIT) {
+ bool en = (drv->chip->flags & NAND_CHIPFLAG_QUAD) != 0;
+ nand_upd_reg(drv, FREG_CFG, FREG_CFG_QUAD_ENABLE,
+ en ? FREG_CFG_QUAD_ENABLE : 0);
+ }
- sfc_lock();
- do {
- memcpy(d->pagebuf, srcbuf, d->chip_data->page_size);
- d->chip_ops->ecc_write(d, d->pagebuf);
+ if(drv->chip->flags & NAND_CHIPFLAG_ON_DIE_ECC) {
+ /* Enable on-die ECC */
+ nand_upd_reg(drv, FREG_CFG, FREG_CFG_ECC_ENABLE, FREG_CFG_ECC_ENABLE);
+ }
- if(d->chip_ops->write_page(d, rowaddr, d->pagebuf) < 0) {
- status = NANDERR_PROGRAM_FAILED;
- goto _end;
- }
+ /* Clear OTP bit to access the main data array */
+ nand_upd_reg(drv, FREG_CFG, FREG_CFG_OTP_ENABLE, 0);
- rowaddr += 1;
- srcbuf += d->chip_data->page_size;
- count -= d->chip_data->page_size;
- } while(count > 0);
+ /* Clear write protection bits */
+ nand_set_reg(drv, FREG_PROT, FREG_PROT_UNLOCK);
- _end:
- sfc_unlock();
- return status;
+ /* Call any chip-specific hooks */
+ if(drv->chip->setup_chip)
+ drv->chip->setup_chip(drv);
}
-int nand_erase_bytes(uint32_t byteaddr, int count)
+int nand_open(struct nand_drv* drv)
{
- nand_drv* d = &nand_driver;
-
- if((d->flags & NAND_DRV_FLAG_WRITEABLE) == 0)
- return NANDERR_WRITE_PROTECTED;
-
- /* Ensure address is aligned to a block boundary */
- if(byteaddr % d->chip_data->page_size)
- return NANDERR_UNALIGNED_ADDRESS;
-
- uint32_t blockaddr = byteaddr / d->chip_data->page_size;
- if(blockaddr % d->chip_data->block_size)
- return NANDERR_UNALIGNED_ADDRESS;
-
- /* Ensure length is also aligned to the size of a block */
- if(count % d->chip_data->page_size)
- return NANDERR_UNALIGNED_LENGTH;
+ if(drv->refcount > 0) {
+ drv->refcount++;
+ return NAND_SUCCESS;
+ }
- count /= d->chip_data->page_size;
- if(count % d->chip_data->block_size)
- return NANDERR_UNALIGNED_LENGTH;
+ /* Initialize the controller */
+ sfc_open();
+ sfc_set_dev_conf(jz_orf(SFC_DEV_CONF,
+ CE_DL(1), HOLD_DL(1), WP_DL(1),
+ CPHA(0), CPOL(0),
+ TSH(15), TSETUP(0), THOLD(0),
+ STA_TYPE_V(1BYTE), CMD_TYPE_V(8BITS),
+ SMP_DELAY(0)));
+ sfc_set_clock(X1000_EXCLK_FREQ);
- count /= d->chip_data->block_size;
+ /* Send the software reset command */
+ sfc_exec(NANDCMD_RESET, 0, NULL, 0);
+ mdelay(10);
- int status = 0;
- sfc_lock();
+ /* Chip identification and setup */
+ if(!identify_chip(drv))
+ return NAND_ERR_UNKNOWN_CHIP;
- for(int i = 0; i < count; ++i) {
- if(d->chip_ops->erase_block(d, blockaddr)) {
- status = NANDERR_ERASE_FAILED;
- goto _end;
- }
+ setup_chip_data(drv);
- /* Advance to next block */
- blockaddr += d->chip_data->block_size;
- }
+ /* Set new SFC parameters */
+ sfc_set_dev_conf(drv->chip->dev_conf);
+ sfc_set_clock(drv->chip->clock_freq);
- _end:
- sfc_unlock();
- return status;
-}
+ /* Enter normal operating mode */
+ setup_chip_registers(drv);
-int nandcmd_read_id(nand_drv* d)
-{
- sfc_op op = {0};
- op.command = NAND_CMD_READ_ID;
- op.flags = SFC_FLAG_READ;
- op.addr_bytes = 1;
- op.addr_lo = 0;
- op.data_bytes = 2;
- op.buffer = d->auxbuf;
- if(sfc_exec(&op))
- return NANDERR_COMMAND_FAILED;
-
- return (d->auxbuf[0] << 8) | d->auxbuf[1];
+ drv->refcount++;
+ return NAND_SUCCESS;
}
-int nandcmd_write_enable(nand_drv* d)
+void nand_close(struct nand_drv* drv)
{
- (void)d;
+ --drv->refcount;
+ if(drv->refcount > 0)
+ return;
- sfc_op op = {0};
- op.command = NAND_CMD_WRITE_ENABLE;
- if(sfc_exec(&op))
- return NANDERR_COMMAND_FAILED;
+ /* Let's reset the chip... the idea is to restore the registers
+ * to whatever they should "normally" be */
+ sfc_exec(NANDCMD_RESET, 0, NULL, 0);
+ mdelay(10);
- return 0;
+ sfc_close();
}
-int nandcmd_get_feature(nand_drv* d, int reg)
+void nand_enable_otp(struct nand_drv* drv, bool enable)
{
- sfc_op op = {0};
- op.command = NAND_CMD_GET_FEATURE;
- op.flags = SFC_FLAG_READ;
- op.addr_bytes = 1;
- op.addr_lo = reg & 0xff;
- op.data_bytes = 1;
- op.buffer = d->auxbuf;
- if(sfc_exec(&op))
- return NANDERR_COMMAND_FAILED;
-
- return d->auxbuf[0];
+ nand_upd_reg(drv, FREG_CFG, FREG_CFG_OTP_ENABLE,
+ enable ? FREG_CFG_OTP_ENABLE : 0);
}
-int nandcmd_set_feature(nand_drv* d, int reg, int val)
+static uint8_t nand_wait_busy(struct nand_drv* drv)
{
- sfc_op op = {0};
- op.command = NAND_CMD_SET_FEATURE;
- op.flags = SFC_FLAG_READ;
- op.addr_bytes = 1;
- op.addr_lo = reg & 0xff;
- op.data_bytes = 1;
- op.buffer = d->auxbuf;
- d->auxbuf[0] = val & 0xff;
- if(sfc_exec(&op))
- return NANDERR_COMMAND_FAILED;
-
- return 0;
+ uint8_t reg;
+ do {
+ reg = nand_get_reg(drv, FREG_STATUS);
+ } while(reg & FREG_STATUS_BUSY);
+ return reg;
}
-int nandcmd_page_read_to_cache(nand_drv* d, uint32_t rowaddr)
+int nand_block_erase(struct nand_drv* drv, nand_block_t block)
{
- sfc_op op = {0};
- op.command = NAND_CMD_PAGE_READ_TO_CACHE;
- op.addr_bytes = d->chip_data->rowaddr_width;
- op.addr_lo = rowaddr;
- if(sfc_exec(&op))
- return NANDERR_COMMAND_FAILED;
-
- return 0;
+ sfc_exec(NANDCMD_WR_EN, 0, NULL, 0);
+ sfc_exec(drv->chip->cmd_block_erase, block, NULL, 0);
+
+ uint8_t status = nand_wait_busy(drv);
+ if(status & FREG_STATUS_EFAIL)
+ return NAND_ERR_ERASE_FAIL;
+ else
+ return NAND_SUCCESS;
}
-int nandcmd_read_from_cache(nand_drv* d, unsigned char* buf)
+int nand_page_program(struct nand_drv* drv, nand_page_t page, const void* buffer)
{
- sfc_op op = {0};
- if(d->chip_data->flags & NANDCHIP_FLAG_QUAD) {
- op.command = NAND_CMD_READ_FROM_CACHEx4;
- op.mode = SFC_MODE_QUAD_IO;
- } else {
- op.command = NAND_CMD_READ_FROM_CACHE;
- op.mode = SFC_MODE_STANDARD;
- }
-
- op.flags = SFC_FLAG_READ;
- op.addr_bytes = d->chip_data->coladdr_width;
- op.addr_lo = 0;
- op.dummy_bits = 8;
- op.data_bytes = d->raw_page_size;
- op.buffer = buf;
- if(sfc_exec(&op))
- return NANDERR_COMMAND_FAILED;
-
- return 0;
+ sfc_exec(NANDCMD_WR_EN, 0, NULL, 0);
+ sfc_exec(drv->chip->cmd_program_load,
+ 0, (void*)buffer, drv->fpage_size|SFC_WRITE);
+ sfc_exec(drv->chip->cmd_program_execute, page, NULL, 0);
+
+ uint8_t status = nand_wait_busy(drv);
+ if(status & FREG_STATUS_PFAIL)
+ return NAND_ERR_PROGRAM_FAIL;
+ else
+ return NAND_SUCCESS;
}
-int nandcmd_program_load(nand_drv* d, const unsigned char* buf)
+int nand_page_read(struct nand_drv* drv, nand_page_t page, void* buffer)
{
- sfc_op op = {0};
- if(d->chip_data->flags & NANDCHIP_FLAG_QUAD) {
- op.command = NAND_CMD_PROGRAM_LOADx4;
- op.mode = SFC_MODE_QUAD_IO;
- } else {
- op.command = NAND_CMD_PROGRAM_LOAD;
- op.mode = SFC_MODE_STANDARD;
- }
+ sfc_exec(drv->chip->cmd_page_read, page, NULL, 0);
+ nand_wait_busy(drv);
+ sfc_exec(drv->chip->cmd_read_cache, 0, buffer, drv->fpage_size|SFC_READ);
- op.flags = SFC_FLAG_WRITE;
- op.addr_bytes = d->chip_data->coladdr_width;
- op.addr_lo = 0;
- op.data_bytes = d->raw_page_size;
- op.buffer = (void*)buf;
- if(sfc_exec(&op))
- return NANDERR_COMMAND_FAILED;
+ if(drv->chip->flags & NAND_CHIPFLAG_ON_DIE_ECC) {
+ uint8_t status = nand_get_reg(drv, FREG_STATUS);
- return 0;
-}
+ if(status & FREG_STATUS_ECC_UNCOR_ERR) {
+ logf("ecc uncorrectable error on page %08lx", (unsigned long)page);
+ return NAND_ERR_ECC_FAIL;
+ }
-int nandcmd_program_execute(nand_drv* d, uint32_t rowaddr)
-{
- sfc_op op = {0};
- op.command = NAND_CMD_PROGRAM_EXECUTE;
- op.addr_bytes = d->chip_data->rowaddr_width;
- op.addr_lo = rowaddr;
- if(sfc_exec(&op))
- return NANDERR_COMMAND_FAILED;
-
- return 0;
-}
+ if(status & FREG_STATUS_ECC_HAS_FLIPS) {
+ logf("ecc corrected bitflips on page %08lx", (unsigned long)page);
+ }
+ }
-int nandcmd_block_erase(nand_drv* d, uint32_t blockaddr)
-{
- sfc_op op = {0};
- op.command = NAND_CMD_BLOCK_ERASE;
- op.addr_bytes = d->chip_data->rowaddr_width;
- op.addr_lo = blockaddr;
- if(sfc_exec(&op))
- return NANDERR_COMMAND_FAILED;
-
- return 0;
+ return NAND_SUCCESS;
}
-const nand_chip_ops nand_chip_ops_std = {
- .open = nandop_std_open,
- .close = nandop_std_close,
- .read_page = nandop_std_read_page,
- .write_page = nandop_std_write_page,
- .erase_block = nandop_std_erase_block,
- .set_wp_enable = nandop_std_set_wp_enable,
- .ecc_read = nandop_ecc_none_read,
- .ecc_write = nandop_ecc_none_write,
-};
-
-/* Helper needed by other ops */
-static int nandop_std_wait_status(nand_drv* d, int errbit)
+int nand_read_bytes(struct nand_drv* drv, uint32_t byte_addr, uint32_t byte_len, void* buffer)
{
- int reg;
- do {
- reg = nandcmd_get_feature(d, NAND_FREG_STATUS);
- if(reg < 0)
- return reg;
- } while(reg & NAND_FREG_STATUS_OIP);
+ if(byte_len == 0)
+ return NAND_SUCCESS;
- if(reg & errbit)
- return NANDERR_OTHER;
-
- return reg;
-}
-
-int nandop_std_open(nand_drv* d)
-{
- (void)d;
- return 0;
-}
+ int rc;
+ unsigned pg_size = drv->chip->page_size;
+ nand_page_t page = byte_addr / pg_size;
+ unsigned offset = byte_addr % pg_size;
+ while(1) {
+ rc = nand_page_read(drv, page, drv->page_buf);
+ if(rc < 0)
+ return rc;
-void nandop_std_close(nand_drv* d)
-{
- (void)d;
-}
+ memcpy(buffer, &drv->page_buf[offset], MIN(pg_size - offset, byte_len));
-int nandop_std_read_page(nand_drv* d, uint32_t rowaddr, unsigned char* buf)
-{
- int status;
+ if(byte_len <= pg_size - offset)
+ break;
- if((status = nandcmd_page_read_to_cache(d, rowaddr)) < 0)
- return status;
- if((status = nandop_std_wait_status(d, 0)) < 0)
- return status;
- if((status = nandcmd_read_from_cache(d, buf)) < 0)
- return status;
+ byte_len -= pg_size - offset;
+ buffer += pg_size - offset;
+ offset = 0;
+ page++;
+ }
- return 0;
+ return NAND_SUCCESS;
}
-int nandop_std_write_page(nand_drv* d, uint32_t rowaddr, const unsigned char* buf)
+int nand_write_bytes(struct nand_drv* drv, uint32_t byte_addr, uint32_t byte_len, const void* buffer)
{
- int status;
-
- if((status = nandcmd_write_enable(d)) < 0)
- return status;
- if((status = nandcmd_program_load(d, buf)) < 0)
- return status;
- if((status = nandcmd_program_execute(d, rowaddr)) < 0)
- return status;
- if((status = nandop_std_wait_status(d, NAND_FREG_STATUS_P_FAIL)) < 0)
- return status;
-
- return 0;
-}
+ if(byte_len == 0)
+ return NAND_SUCCESS;
-int nandop_std_erase_block(nand_drv* d, uint32_t blockaddr)
-{
- int status;
+ int rc;
+ unsigned pg_size = drv->chip->page_size;
+ unsigned blk_size = pg_size << drv->chip->log2_ppb;
- if((status = nandcmd_write_enable(d)) < 0)
- return status;
- if((status = nandcmd_block_erase(d, blockaddr)) < 0)
- return status;
- if((status = nandop_std_wait_status(d, NAND_FREG_STATUS_E_FAIL) < 0))
- return status;
+ if(byte_addr % blk_size != 0)
+ return NAND_ERR_UNALIGNED;
+ if(byte_len % blk_size != 0)
+ return NAND_ERR_UNALIGNED;
- return 0;
-}
+ nand_page_t page = byte_addr / pg_size;
+ nand_page_t end_page = page + (byte_len / pg_size);
-int nandop_std_set_wp_enable(nand_drv* d, bool en)
-{
- int val = nandcmd_get_feature(d, NAND_FREG_PROTECTION);
- if(val < 0)
- return val;
-
- if(en) {
- val &= ~NAND_FREG_PROTECTION_ALLBP;
- if(d->chip_data->flags & NANDCHIP_FLAG_USE_BRWD)
- val &= ~NAND_FREG_PROTECTION_BRWD;
- } else {
- val |= NAND_FREG_PROTECTION_ALLBP;
- if(d->chip_data->flags & NANDCHIP_FLAG_USE_BRWD)
- val |= NAND_FREG_PROTECTION_BRWD;
+ for(nand_block_t blk = page; blk < end_page; blk += drv->ppb) {
+ rc = nand_block_erase(drv, blk);
+ if(rc < 0)
+ return rc;
}
- sfc_set_wp_enable(false);
- int status = nandcmd_set_feature(d, NAND_FREG_PROTECTION, val);
- sfc_set_wp_enable(true);
-
- if(status < 0)
- return status;
+ for(; page != end_page; ++page) {
+ memcpy(drv->page_buf, buffer, pg_size);
+ memset(&drv->page_buf[pg_size], 0xff, drv->chip->oob_size);
+ buffer += pg_size;
- return 0;
-}
+ rc = nand_page_program(drv, page, drv->page_buf);
+ if(rc < 0)
+ return rc;
+ }
-int nandop_ecc_none_read(nand_drv* d, unsigned char* buf)
-{
- (void)d;
- (void)buf;
- return 0;
+ return NAND_SUCCESS;
}
-void nandop_ecc_none_write(nand_drv* d, unsigned char* buf)
-{
- memset(&buf[d->chip_data->page_size], 0xff, d->chip_data->spare_size);
-}
+/* TODO - NAND driver future improvements
+ *
+ * 1. Support sofware or on-die ECC transparently. Support debug ECC bypass.
+ *
+ * It's probably best to add an API call to turn ECC on or off. Software
+ * ECC and most or all on-die ECC implementations require some OOB bytes
+ * to function; which leads us to the next problem...
+ *
+ * 2. Allow safe access to OOB areas
+ *
+ * The OOB data area is not fully available to users; it is also occupied
+ * by ECC data and bad block markings. The NAND driver needs to provide a
+ * mapping which allows OOB data users to map around those reserved areas,
+ * otherwise it's not really possible to use OOB data.
+ *
+ * 3. Support partial page programming.
+ *
+ * This might already work. My understanding of NAND flash is that bits are
+ * represented by charge deposited on flash cells. In the case of SLC flash,
+ * cells are one bit. For MLC flash, cells can store more than one bit; but
+ * MLC flash is much less reliable than SLC. We probably don't have to be
+ * concerned about MLC flash, and its does not support partial programming
+ * anyway due to the cell characteristics, so I will only consider SLC here.
+ *
+ * For SLC there are two cell states -- an uncharged cell represents a "1"
+ * and a charged cell represents "0". Programming can only deposit charge
+ * on a cell and erasing can only remove charge. Therefore, "programming" a
+ * cell to 1 is actually a no-op.
+ *
+ * So, there's no datasheet which spells this out, but I suspect you just
+ * set the areas you're not interested in programming to 0xff. Programming
+ * can never change a written 0 back to a 1, so programming a 1 bit works
+ * more like a "don't care" (= keep whatever value is already there).
+ *
+ * What _is_ given by the datasheets is limits on how many times you can
+ * reprogram the same page without erasing it. This is an overall limit
+ * called NOP (number of programs) in many datasheets. In addition to this,
+ * sub-regions of the page have further limits: it's common for a 2048+64
+ * byte page to be split into 8 regions, with four 512-byte main areas and
+ * four 16-byte OOB areas. Usually, each subregion can only be programmed
+ * once. However, you can write multiple subregions with a single program.
+ *
+ * Violating programming constraints could cause data loss, so we need to
+ * communicate to upper layers what the limitations are here if they want
+ * to use partial programming safely.
+ *
+ * Programming the same page more than once increases the overall stress
+ * on the flash cells and can cause bitflips. For this reason, it's best
+ * to keep the number of programs as low as possible. Some sources suggest
+ * that programming the pages in a block in linear order is also better to
+ * reduce stress, although I don't know why this would be.
+ *
+ * These program/read stresses can flip bits, but it's only due to residual
+ * charge building up on uncharged cells; cells are not permanently damaged
+ * by these kind of stresses. Erasing the block will remove the charge and
+ * restore all the cells to a clean state.
+ *
+ * These slides are fairly informative on this subject:
+ * - https://cushychicken.github.io/assets/cooke_inconvenient_truths.pdf
+ *
+ * 4. Bad block management
+ *
+ * This probably doesn't belong in the NAND layer but it seems wise to keep
+ * at least a bad block table at the level of the NAND driver. Factory bad
+ * block marks are usually some non-0xFF byte in the OOB area, but bad blocks
+ * which develop over the device lifetime usually won't be marked; after all
+ * they are unreliable, so we can't program a marking on them and expect it
+ * to stick. So, most FTL systems keep a bad block table somewhere in flash
+ * and update it whenever a block goes bad.
+ *
+ * So, in addition to a bad block marker scan, we should try to gather bad
+ * block information from such tables.
+ */
diff --git a/firmware/target/mips/ingenic_x1000/nand-x1000.h b/firmware/target/mips/ingenic_x1000/nand-x1000.h
index f6709aaaf9..0ccd075079 100644
--- a/firmware/target/mips/ingenic_x1000/nand-x1000.h
+++ b/firmware/target/mips/ingenic_x1000/nand-x1000.h
@@ -22,193 +22,221 @@
#ifndef __NAND_X1000_H__
#define __NAND_X1000_H__
-/* NOTE: this is a very minimal API designed only to support a bootloader.
- * Not suitable for general data storage. It doesn't have proper support for
- * partial page writes, access to spare area, etc, which are all necessary
- * for an effective flash translation layer.
- */
-
#include <stdint.h>
-#include <stdbool.h>
#include <stddef.h>
-#include "nand-x1000-err.h"
-
-/* Chip supports quad I/O for page read/write */
-#define NANDCHIP_FLAG_QUAD 0x01
-
-/* Set/clear the BRWD bit when enabling/disabling write protection */
-#define NANDCHIP_FLAG_USE_BRWD 0x02
-
-typedef struct nand_drv nand_drv;
-
-/* Defines some static information about a NAND chip */
-typedef struct nand_chip_data {
- const char* name; /* Name for debugging purposes */
- uint8_t mf_id; /* Manufacturer ID */
- uint8_t dev_id; /* Device ID */
- uint8_t rowaddr_width; /* Number of bytes in row addresses */
- uint8_t coladdr_width; /* Number of bytes in column addresses */
- uint32_t dev_conf; /* Value to write to SFC_DEV_CONF register */
- uint32_t clock_freq; /* Frequency to switch to after identification */
- uint32_t block_size; /* Number of pages per eraseblock */
- uint32_t page_size; /* Number of data bytes per page */
- uint32_t spare_size; /* Number of spare bytes per page */
- int flags; /* Various flags */
-} nand_chip_data;
-
-/* Defines high-level operations used to implement the public API.
- * Chips may need to override operations if the default ones aren't suitable.
+#include <stdbool.h>
+#include "kernel.h"
+
+#define NAND_SUCCESS 0
+#define NAND_ERR_UNKNOWN_CHIP (-1)
+#define NAND_ERR_PROGRAM_FAIL (-2)
+#define NAND_ERR_ERASE_FAIL (-3)
+#define NAND_ERR_UNALIGNED (-4)
+#define NAND_ERR_ECC_FAIL (-5)
+
+/* keep max page size in sync with the NAND chip table in the .c file */
+#define NAND_DRV_SCRATCHSIZE 32
+#define NAND_DRV_MAXPAGESIZE 2112
+
+/* Quad I/O support bit */
+#define NAND_CHIPFLAG_QUAD 0x0001
+/* Chip requires QE bit set to enable quad I/O mode */
+#define NAND_CHIPFLAG_HAS_QE_BIT 0x0002
+/* True if the chip has on-die ECC */
+#define NAND_CHIPFLAG_ON_DIE_ECC 0x0004
+
+/* cmd mode a d phase format has data */
+#define NANDCMD_RESET SFC_CMD(0xff, SFC_TMODE_1_1_1, 0, 0, SFC_PFMT_ADDR_FIRST, 0)
+#define NANDCMD_READID_OPCODE SFC_CMD(0x9f, SFC_TMODE_1_1_1, 0, 0, SFC_PFMT_ADDR_FIRST, 1)
+#define NANDCMD_READID_ADDR SFC_CMD(0x9f, SFC_TMODE_1_1_1, 1, 0, SFC_PFMT_ADDR_FIRST, 1)
+#define NANDCMD_READID_DUMMY SFC_CMD(0x9f, SFC_TMODE_1_1_1, 0, 8, SFC_PFMT_ADDR_FIRST, 1)
+#define NANDCMD_WR_EN SFC_CMD(0x06, SFC_TMODE_1_1_1, 0, 0, SFC_PFMT_ADDR_FIRST, 0)
+#define NANDCMD_GET_FEATURE SFC_CMD(0x0f, SFC_TMODE_1_1_1, 1, 0, SFC_PFMT_ADDR_FIRST, 1)
+#define NANDCMD_SET_FEATURE SFC_CMD(0x1f, SFC_TMODE_1_1_1, 1, 0, SFC_PFMT_ADDR_FIRST, 1)
+#define NANDCMD_PAGE_READ SFC_CMD(0x13, SFC_TMODE_1_1_1, 3, 0, SFC_PFMT_ADDR_FIRST, 0)
+#define NANDCMD_READ_CACHE_SLOW SFC_CMD(0x03, SFC_TMODE_1_1_1, 2, 8, SFC_PFMT_ADDR_FIRST, 1)
+#define NANDCMD_READ_CACHE SFC_CMD(0x0b, SFC_TMODE_1_1_1, 2, 8, SFC_PFMT_ADDR_FIRST, 1)
+#define NANDCMD_READ_CACHE_x4 SFC_CMD(0x6b, SFC_TMODE_1_1_4, 2, 8, SFC_PFMT_ADDR_FIRST, 1)
+#define NANDCMD_PROGRAM_EXECUTE SFC_CMD(0x10, SFC_TMODE_1_1_1, 3, 0, SFC_PFMT_ADDR_FIRST, 0)
+#define NANDCMD_PROGRAM_LOAD SFC_CMD(0x02, SFC_TMODE_1_1_1, 2, 0, SFC_PFMT_ADDR_FIRST, 1)
+#define NANDCMD_PROGRAM_LOAD_x4 SFC_CMD(0x32, SFC_TMODE_1_1_4, 2, 0, SFC_PFMT_ADDR_FIRST, 1)
+#define NANDCMD_BLOCK_ERASE SFC_CMD(0xd8, SFC_TMODE_1_1_1, 3, 0, SFC_PFMT_ADDR_FIRST, 0)
+
+/* Feature registers are found in linux/mtd/spinand.h,
+ * apparently these are pretty standardized */
+#define FREG_PROT 0xa0
+#define FREG_PROT_UNLOCK 0x00
+
+#define FREG_CFG 0xb0
+#define FREG_CFG_OTP_ENABLE (1 << 6)
+#define FREG_CFG_ECC_ENABLE (1 << 4)
+#define FREG_CFG_QUAD_ENABLE (1 << 0)
+
+/* Winbond-specific bit used on the W25N01GVxx */
+#define FREG_CFG_WINBOND_BUF (1 << 3)
+
+#define FREG_STATUS 0xc0
+#define FREG_STATUS_BUSY (1 << 0)
+#define FREG_STATUS_EFAIL (1 << 2)
+#define FREG_STATUS_PFAIL (1 << 3)
+#define FREG_STATUS_ECC_MASK (3 << 4)
+#define FREG_STATUS_ECC_NO_FLIPS (0 << 4)
+#define FREG_STATUS_ECC_HAS_FLIPS (1 << 4)
+#define FREG_STATUS_ECC_UNCOR_ERR (2 << 4)
+
+/* Types to distinguish between block & page addresses in the API.
*
- * Negative return codes return an error, while zero or positive codes are
- * considered successful. This allows a function to return meaningful data,
- * if applicable.
+ * BIT 31 log2_ppb bits
+ * +-------------------------------+---------------+
+ * nand_page_t = | block nr | page nr |
+ * +-------------------------------+---------------+
+ * BIT 0
+ *
+ * The page address is split into block and page numbers. Page numbers occupy
+ * the lower log2_ppb bits, and the block number occupies the upper bits.
+ *
+ * Block addresses are structured the same as page addresses, but with a page
+ * number of 0. So block number N has address N << log2_ppb.
*/
-typedef struct nand_chip_ops {
- /* Called once after identifying the chip */
- int(*open)(nand_drv* d);
+typedef uint32_t nand_block_t;
+typedef uint32_t nand_page_t;
- /* Called once when the driver is closed */
- void(*close)(nand_drv* d);
+struct nand_drv;
- /* Read or write a complete page including both main and spare areas. */
- int(*read_page)(nand_drv* d, uint32_t rowaddr, unsigned char* buf);
- int(*write_page)(nand_drv* d, uint32_t rowaddr, const unsigned char* buf);
+struct nand_chip {
+ /* Base2 logarithm of the number of pages per block */
+ unsigned log2_ppb;
- /* Erase a block. */
- int(*erase_block)(nand_drv* d, uint32_t blockaddr);
+ /* Size of a page's main / oob areas, in bytes. */
+ unsigned page_size;
+ unsigned oob_size;
- /* Enable or disable the chip's write protection. */
- int(*set_wp_enable)(nand_drv* d, bool en);
+ /* Total number of blocks in the chip */
+ unsigned nr_blocks;
- /* Perform error correction and detection on a raw page (main + spare).
- * Return the number of errors detected and corrected, or a negative value
- * if errors were detected but could not be corrected.
- */
- int(*ecc_read)(nand_drv* d, unsigned char* buf);
+ /* Bad block marker offset within the 1st page of a bad block */
+ unsigned bbm_pos;
+
+ /* Clock frequency to use */
+ uint32_t clock_freq;
+
+ /* Value of sfc_dev_conf */
+ uint32_t dev_conf;
+
+ /* Chip specific flags */
+ uint32_t flags;
+
+ /* SFC commands for issuing I/O ops */
+ uint32_t cmd_page_read;
+ uint32_t cmd_program_execute;
+ uint32_t cmd_block_erase;
+ uint32_t cmd_read_cache;
+ uint32_t cmd_program_load;
+
+ /* Chip-specific setup routine */
+ void(*setup_chip)(struct nand_drv* drv);
+};
+
+enum nand_readid_method {
+ NAND_READID_OPCODE,
+ NAND_READID_ADDR,
+ NAND_READID_DUMMY,
+};
+
+struct nand_chip_id {
+ uint8_t method;
+ uint8_t num_id_bytes;
+ uint8_t id_bytes[4];
+ const struct nand_chip* chip;
+};
+
+#define NAND_CHIP_ID(_chip, _method, ...) \
+ { .method = _method, \
+ .num_id_bytes = ARRAYLEN(((uint8_t[]){__VA_ARGS__})), \
+ .id_bytes = {__VA_ARGS__}, \
+ .chip = _chip }
- /* Generate ECC data for a page. The buffer main area is already filled
- * and this function should write ECC data into the spare area.
- */
- void(*ecc_write)(nand_drv* d, unsigned char* buf);
-} nand_chip_ops;
-
-/* Struct used to list all supported NAND chips in an array */
-typedef struct nand_chip_desc {
- const nand_chip_data* data;
- const nand_chip_ops* ops;
-} nand_chip_desc;
-
-/* NAND driver structure. It can be accessed by chip ops, but they must not
- * modify any fields except for "auxbuf", which is a small buffer that can
- * be used for commands that need to read/write small amounts of data: often
- * needed for polling status, etc.
- */
struct nand_drv {
- const nand_chip_ops* chip_ops;
- const nand_chip_data* chip_data;
- unsigned char* pagebuf;
- unsigned char* auxbuf;
- uint32_t raw_page_size;
- int flags;
+ /* NAND access lock. Needs to be held during any operations. */
+ struct mutex mutex;
+
+ /* Reference count for open/close operations */
+ unsigned refcount;
+
+ /* Scratch and page buffers. Both need to be cacheline-aligned and are
+ * provided externally by the caller prior to nand_open().
+ *
+ * - The scratch buffer is NAND_DRV_SCRATCHSIZE bytes long and is used
+ * for small data transfers associated with commands. It must not be
+ * disturbed while any NAND operation is in progress.
+ *
+ * - The page buffer is used by certain functions like nand_read_bytes(),
+ * but it's main purpose is to provide a common temporary buffer for
+ * driver users to perform I/O with. Must be fpage_size bytes long.
+ */
+ uint8_t* scratch_buf;
+ uint8_t* page_buf;
+
+ /* Pointer to the chip data. */
+ const struct nand_chip* chip;
+
+ /* Pages per block = 1 << chip->log2_ppb */
+ unsigned ppb;
+
+ /* Full page size = chip->page_size + chip->oob_size */
+ unsigned fpage_size;
};
-/* Note: sfc_init() must be called prior to nand_open() */
-extern int nand_open(void);
-extern void nand_close(void);
+extern const struct nand_chip_id supported_nand_chips[];
+extern const size_t nr_supported_nand_chips;
-/* Controls device-side write protection registers as well as software lock.
- * Erase and program operations will fail unless you first enable writes.
+/* Return the static NAND driver instance.
+ *
+ * ALL normal Rockbox code should use this instance. The SPL does not
+ * use it, because it needs to manually place buffers in external RAM.
*/
-extern int nand_enable_writes(bool en);
-
-/* Byte-based NAND operations */
-extern int nand_read_bytes(uint32_t byteaddr, int count, void* buf);
-extern int nand_write_bytes(uint32_t byteaddr, int count, const void* buf);
-extern int nand_erase_bytes(uint32_t byteaddr, int count);
-
-/* NAND command numbers */
-#define NAND_CMD_READ_ID 0x9f
-#define NAND_CMD_WRITE_ENABLE 0x06
-#define NAND_CMD_GET_FEATURE 0x0f
-#define NAND_CMD_SET_FEATURE 0x1f
-#define NAND_CMD_PAGE_READ_TO_CACHE 0x13
-#define NAND_CMD_READ_FROM_CACHE 0x0b
-#define NAND_CMD_READ_FROM_CACHEx4 0x6b
-#define NAND_CMD_PROGRAM_LOAD 0x02
-#define NAND_CMD_PROGRAM_LOADx4 0x32
-#define NAND_CMD_PROGRAM_EXECUTE 0x10
-#define NAND_CMD_BLOCK_ERASE 0xd8
-
-/* NAND device register addresses for GET_FEATURE / SET_FEATURE */
-#define NAND_FREG_PROTECTION 0xa0
-#define NAND_FREG_FEATURE 0xb0
-#define NAND_FREG_STATUS 0xc0
-
-/* Protection register bits */
-#define NAND_FREG_PROTECTION_BRWD 0x80
-#define NAND_FREG_PROTECTION_BP2 0x20
-#define NAND_FREG_PROTECTION_BP1 0x10
-#define NAND_FREG_PROTECTION_BP0 0x80
-/* Mask of BP bits 0-2 */
-#define NAND_FREG_PROTECTION_ALLBP (0x38)
-
-/* Feature register bits */
-#define NAND_FREG_FEATURE_QE 0x01
-
-/* Status register bits */
-#define NAND_FREG_STATUS_OIP 0x01
-#define NAND_FREG_STATUS_WEL 0x02
-#define NAND_FREG_STATUS_E_FAIL 0x04
-#define NAND_FREG_STATUS_P_FAIL 0x08
-
-/* Standard implementations for low-level NAND commands. I'm not aware of any
- * actual standard governing these, but it seems many vendors follow the same
- * command numbering, status bits, and behavior so these implementations should
- * work across a wide variety of chips.
+extern struct nand_drv* nand_init(void);
+
+static inline void nand_lock(struct nand_drv* drv)
+{
+ mutex_lock(&drv->mutex);
+}
+
+static inline void nand_unlock(struct nand_drv* drv)
+{
+ mutex_unlock(&drv->mutex);
+}
+
+/* Open or close the NAND driver
*
- * If adding a new NAND chip which only has a minor deviation from these
- * standard implementations, consider adding a flag and modifying these
- * functions to change their behavior based on the flag, instead of writing
- * a whole new implementation.
+ * The NAND driver is reference counted, and opening / closing it will
+ * increment and decrement the reference count. The hardware is only
+ * controlled when the reference count rises above or falls to 0, else
+ * these functions are no-ops which always succeed.
*
- * None of these functions are directly called by the high-level driver code,
- * except for nandcmd_std_read_id(). They can be used to implement higher-level
- * functions in a device's "nand_chip_ops".
+ * These functions require the lock to be held.
*/
-extern int nandcmd_read_id(nand_drv* d);
-extern int nandcmd_write_enable(nand_drv* d);
-extern int nandcmd_get_feature(nand_drv* d, int reg);
-extern int nandcmd_set_feature(nand_drv* d, int reg, int val);
-extern int nandcmd_page_read_to_cache(nand_drv* d, uint32_t rowaddr);
-extern int nandcmd_read_from_cache(nand_drv* d, unsigned char* buf);
-extern int nandcmd_program_load(nand_drv* d, const unsigned char* buf);
-extern int nandcmd_program_execute(nand_drv* d, uint32_t rowaddr);
-extern int nandcmd_block_erase(nand_drv* d, uint32_t blockaddr);
-
-/* Table filled with all the standard operations for chips which don't
- * need to override any operations.
- */
-extern const nand_chip_ops nand_chip_ops_std;
+extern int nand_open(struct nand_drv* drv);
+extern void nand_close(struct nand_drv* drv);
+
+/* Enable/disable OTP access. OTP data pages are usually vendor-specific. */
+void nand_enable_otp(struct nand_drv* drv, bool enable);
-/* Standard NAND chip ops based on the standard "nandcmd" functions.
+/* Read / program / erase operations. Buffer needs to be cache-aligned for DMA.
+ * Read and program operate on full page data, ie. including OOB data areas.
*
- * Same advice applies here as there: if it's possible to support minor
- * chip variations with a flag, modify these functions to do so.
+ * NOTE: ECC is not implemented. If it ever needs to be, these functions will
+ * probably use ECC transparently. All code should be written to expect this.
*/
-extern int nandop_std_open(nand_drv* d);
-extern void nandop_std_close(nand_drv* d);
-extern int nandop_std_read_page(nand_drv* d, uint32_t rowaddr, unsigned char* buf);
-extern int nandop_std_write_page(nand_drv* d, uint32_t rowaddr, const unsigned char* buf);
-extern int nandop_std_erase_block(nand_drv* d, uint32_t blockaddr);
-extern int nandop_std_set_wp_enable(nand_drv* d, bool en);
-
-/* The default ECC implementation is a no-op: reads always succeed without
- * reporting errors and writes will fill the spare area with '0xff' bytes.
- *
- * For chips that support internal ECC, this often works because the chip will
- * ignore writes to the ECC areas.
+extern int nand_block_erase(struct nand_drv* drv, nand_block_t block);
+extern int nand_page_program(struct nand_drv* drv, nand_page_t page, const void* buffer);
+extern int nand_page_read(struct nand_drv* drv, nand_page_t page, void* buffer);
+
+/* Wrappers to read/write bytes. For simple access to the main data area only.
+ * The write address / length must align to a block boundary. Reads do not have
+ * any alignment requirement. OOB data is never read, and is written as 0xff.
*/
-extern int nandop_ecc_none_read(nand_drv* d, unsigned char* buf);
-extern void nandop_ecc_none_write(nand_drv* d, unsigned char* buf);
+extern int nand_read_bytes(struct nand_drv* drv, uint32_t byte_addr, uint32_t byte_len, void* buffer);
+extern int nand_write_bytes(struct nand_drv* drv, uint32_t byte_addr, uint32_t byte_len, const void* buffer);
#endif /* __NAND_X1000_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/pcm-x1000.c b/firmware/target/mips/ingenic_x1000/pcm-x1000.c
index 9ae6f5a956..4b9cb85b57 100644
--- a/firmware/target/mips/ingenic_x1000/pcm-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/pcm-x1000.c
@@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Copyright (C) 2021 Aidan MacDonald
+ * Copyright (C) 2021-2022 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -21,6 +21,7 @@
#include "system.h"
#include "kernel.h"
+#include "audio.h"
#include "audiohw.h"
#include "pcm.h"
#include "pcm-internal.h"
@@ -28,34 +29,68 @@
#include "dma-x1000.h"
#include "irq-x1000.h"
#include "x1000/aic.h"
+#include "x1000/cpm.h"
-#define AIC_STATE_STOPPED 0
-#define AIC_STATE_PLAYING 1
+#define AIC_STATE_STOPPED 0x00
+#define AIC_STATE_PLAYING 0x01
+#define AIC_STATE_RECORDING 0x02
volatile unsigned aic_tx_underruns = 0;
static int aic_state = AIC_STATE_STOPPED;
-static int aic_lock = 0;
-static volatile int aic_dma_pending_event = DMA_EVENT_NONE;
+static int play_lock = 0;
+static volatile int play_dma_pending_event = DMA_EVENT_NONE;
+static dma_desc play_dma_desc;
+static void pcm_play_dma_int_cb(int event);
-static dma_desc aic_dma_desc;
+#ifdef HAVE_RECORDING
+volatile unsigned aic_rx_overruns = 0;
+static int rec_lock = 0;
+static volatile int rec_dma_pending_event = DMA_EVENT_NONE;
+static dma_desc rec_dma_desc;
-static void pcm_dma_int_cb(int event);
+static void pcm_rec_dma_int_cb(int event);
+#endif
void pcm_play_dma_init(void)
{
+ /* Ungate clock */
+ jz_writef(CPM_CLKGR, AIC(0));
+
+ /* Configure AIC with some sane defaults */
+ jz_writef(AIC_CFG, RST(1));
+ jz_writef(AIC_I2SCR, STPBK(1));
+ jz_writef(AIC_CFG, MSB(0), LSMP(0), ICDC(0), AUSEL(1), BCKD(0), SYNCD(0));
+ jz_writef(AIC_CCR, ENDSW(0), ASVTSU(0));
+ jz_writef(AIC_I2SCR, RFIRST(0), ESCLK(0), AMSL(0));
+ jz_write(AIC_SPENA, 0);
+
/* Let the target initialize its hardware and setup the AIC */
audiohw_init();
- /* Set DMA callback */
- dma_set_callback(DMA_CHANNEL_AUDIO, pcm_dma_int_cb);
+#if (PCM_NATIVE_BITDEPTH > 16)
+ /* Program audio format (stereo, 24 bit samples) */
+ jz_writef(AIC_CCR, PACK16(0), CHANNEL_V(STEREO),
+ OSS_V(24BIT), ISS_V(24BIT), M2S(0));
+ jz_writef(AIC_I2SCR, SWLH(0));
+#else
+ /* Program audio format (stereo, packed 16 bit samples) */
+ jz_writef(AIC_CCR, PACK16(1), CHANNEL_V(STEREO),
+ OSS_V(16BIT), ISS_V(16BIT), M2S(0));
+ jz_writef(AIC_I2SCR, SWLH(0));
+#endif
- /* Program FIFO threshold -- DMA settings must match */
- jz_writef(AIC_CFG, TFTH(16));
+ /* Set DMA settings */
+ jz_writef(AIC_CFG, TFTH(16), RFTH(15));
+ dma_set_callback(DMA_CHANNEL_AUDIO, pcm_play_dma_int_cb);
+#ifdef HAVE_RECORDING
+ dma_set_callback(DMA_CHANNEL_RECORD, pcm_rec_dma_int_cb);
+#endif
- /* Ensure all playback is disabled */
- jz_writef(AIC_CCR, ERPL(0));
+ /* Mask all interrupts and disable playback/recording */
+ jz_writef(AIC_CCR, EROR(0), ETUR(0), ERFS(0), ETFS(0),
+ ENLBF(0), ERPL(0), EREC(0));
/* Enable the controller */
jz_writef(AIC_CFG, ENABLE(1));
@@ -74,23 +109,23 @@ void pcm_dma_apply_settings(void)
audiohw_set_frequency(pcm_fsel);
}
-static void pcm_dma_start(const void* addr, size_t size)
+static void play_dma_start(const void* addr, size_t size)
{
- aic_dma_desc.cm = jz_orf(DMA_CHN_CM, SAI(1), DAI(0), RDIL(9),
- SP_V(32BIT), DP_V(32BIT), TSZ_V(AUTO),
- STDE(0), TIE(1), LINK(0));
- aic_dma_desc.sa = PHYSADDR(addr);
- aic_dma_desc.ta = PHYSADDR(JA_AIC_DR);
- aic_dma_desc.tc = size;
- aic_dma_desc.sd = 0;
- aic_dma_desc.rt = jz_orf(DMA_CHN_RT, TYPE_V(I2S_TX));
- aic_dma_desc.pad0 = 0;
- aic_dma_desc.pad1 = 0;
-
- commit_dcache_range(&aic_dma_desc, sizeof(dma_desc));
+ play_dma_desc.cm = jz_orf(DMA_CHN_CM, SAI(1), DAI(0), RDIL(9),
+ SP_V(32BIT), DP_V(32BIT), TSZ_V(AUTO),
+ STDE(0), TIE(1), LINK(0));
+ play_dma_desc.sa = PHYSADDR(addr);
+ play_dma_desc.ta = PHYSADDR(JA_AIC_DR);
+ play_dma_desc.tc = size;
+ play_dma_desc.sd = 0;
+ play_dma_desc.rt = jz_orf(DMA_CHN_RT, TYPE_V(I2S_TX));
+ play_dma_desc.pad0 = 0;
+ play_dma_desc.pad1 = 0;
+
+ commit_dcache_range(&play_dma_desc, sizeof(dma_desc));
commit_dcache_range(addr, size);
- REG_DMA_CHN_DA(DMA_CHANNEL_AUDIO) = PHYSADDR(&aic_dma_desc);
+ REG_DMA_CHN_DA(DMA_CHANNEL_AUDIO) = PHYSADDR(&play_dma_desc);
jz_writef(DMA_CHN_CS(DMA_CHANNEL_AUDIO), DES8(1), NDES(0));
jz_set(DMA_DB, 1 << DMA_CHANNEL_AUDIO);
jz_writef(DMA_CHN_CS(DMA_CHANNEL_AUDIO), CTE(1));
@@ -98,13 +133,13 @@ static void pcm_dma_start(const void* addr, size_t size)
pcm_play_dma_status_callback(PCM_DMAST_STARTED);
}
-static void pcm_dma_handle_event(int event)
+static void play_dma_handle_event(int event)
{
if(event == DMA_EVENT_COMPLETE) {
const void* addr;
size_t size;
if(pcm_play_dma_complete_callback(PCM_DMAST_OK, &addr, &size))
- pcm_dma_start(addr, size);
+ play_dma_start(addr, size);
} else if(event == DMA_EVENT_NONE) {
/* ignored, so callers don't need to check for this */
} else {
@@ -112,54 +147,192 @@ static void pcm_dma_handle_event(int event)
}
}
-static void pcm_dma_int_cb(int event)
+static void pcm_play_dma_int_cb(int event)
{
- if(aic_lock) {
- aic_dma_pending_event = event;
+ if(play_lock) {
+ play_dma_pending_event = event;
return;
} else {
- pcm_dma_handle_event(event);
+ play_dma_handle_event(event);
}
}
void pcm_play_dma_start(const void* addr, size_t size)
{
- aic_dma_pending_event = DMA_EVENT_NONE;
- aic_state = AIC_STATE_PLAYING;
+ play_dma_pending_event = DMA_EVENT_NONE;
+ aic_state |= AIC_STATE_PLAYING;
- pcm_dma_start(addr, size);
+ play_dma_start(addr, size);
jz_writef(AIC_CCR, TDMS(1), ETUR(1), ERPL(1));
}
void pcm_play_dma_stop(void)
{
- jz_writef(AIC_CCR, TDMS(0), ETUR(0), ERPL(0));
- jz_writef(AIC_CCR, TFLUSH(1));
+ /* disable DMA and underrun interrupts */
+ jz_writef(AIC_CCR, TDMS(0), ETUR(0));
+
+ /*
+ * wait for FIFO to be empty - on targets
+ * with >16bit samples, flushing the fifo
+ * may result in swapping l and r channels!
+ * (ensure bit clock is running first)
+ */
+ if (jz_readf(AIC_I2SCR, STPBK) == 0) {
+ while(jz_readf(AIC_SR, TFL) != 0);
+ } else {
+ panicf("pcm_play_dma_stop: No bit clock running!");
+ }
+
+ /* disable playback */
+ jz_writef(AIC_CCR, ERPL(0));
- aic_dma_pending_event = DMA_EVENT_NONE;
- aic_state = AIC_STATE_STOPPED;
+ play_dma_pending_event = DMA_EVENT_NONE;
+ aic_state &= ~AIC_STATE_PLAYING;
}
void pcm_play_lock(void)
{
- ++aic_lock;
+ int irq = disable_irq_save();
+ ++play_lock;
+ restore_irq(irq);
}
void pcm_play_unlock(void)
{
int irq = disable_irq_save();
- if(--aic_lock == 0 && aic_state == AIC_STATE_PLAYING) {
- pcm_dma_handle_event(aic_dma_pending_event);
- aic_dma_pending_event = DMA_EVENT_NONE;
+ if(--play_lock == 0 && (aic_state & AIC_STATE_PLAYING)) {
+ play_dma_handle_event(play_dma_pending_event);
+ play_dma_pending_event = DMA_EVENT_NONE;
}
restore_irq(irq);
}
+#ifdef HAVE_RECORDING
+/*
+ * Recording
+ */
+
+static void rec_dma_start(void* addr, size_t size)
+{
+ /* NOTE: Rockbox always records in stereo and the AIC pushes in the
+ * sample for each channel separately. One frame therefore requires
+ * two 16-bit transfers from the AIC. */
+ rec_dma_desc.cm = jz_orf(DMA_CHN_CM, SAI(0), DAI(1), RDIL(6),
+ SP_V(16BIT), DP_V(16BIT), TSZ_V(16BIT),
+ STDE(0), TIE(1), LINK(0));
+ rec_dma_desc.sa = PHYSADDR(JA_AIC_DR);
+ rec_dma_desc.ta = PHYSADDR(addr);
+ rec_dma_desc.tc = size / 2;
+ rec_dma_desc.sd = 0;
+ rec_dma_desc.rt = jz_orf(DMA_CHN_RT, TYPE_V(I2S_RX));
+ rec_dma_desc.pad0 = 0;
+ rec_dma_desc.pad1 = 0;
+
+ commit_dcache_range(&rec_dma_desc, sizeof(dma_desc));
+ if((unsigned long)addr < 0xa0000000ul)
+ discard_dcache_range(addr, size);
+
+ REG_DMA_CHN_DA(DMA_CHANNEL_RECORD) = PHYSADDR(&rec_dma_desc);
+ jz_writef(DMA_CHN_CS(DMA_CHANNEL_RECORD), DES8(1), NDES(0));
+ jz_set(DMA_DB, 1 << DMA_CHANNEL_RECORD);
+ jz_writef(DMA_CHN_CS(DMA_CHANNEL_RECORD), CTE(1));
+
+ pcm_rec_dma_status_callback(PCM_DMAST_STARTED);
+}
+
+static void rec_dma_handle_event(int event)
+{
+ if(event == DMA_EVENT_COMPLETE) {
+ void* addr;
+ size_t size;
+ if(pcm_rec_dma_complete_callback(PCM_DMAST_OK, &addr, &size))
+ rec_dma_start(addr, size);
+ } else if(event == DMA_EVENT_NONE) {
+ /* ignored, so callers don't need to check for this */
+ } else {
+ pcm_rec_dma_status_callback(PCM_DMAST_ERR_DMA);
+ }
+}
+
+static void pcm_rec_dma_int_cb(int event)
+{
+ if(rec_lock) {
+ rec_dma_pending_event = event;
+ return;
+ } else {
+ rec_dma_handle_event(event);
+ }
+}
+
+void pcm_rec_dma_init(void)
+{
+}
+
+void pcm_rec_dma_close(void)
+{
+}
+
+void pcm_rec_dma_start(void* addr, size_t size)
+{
+ rec_dma_pending_event = DMA_EVENT_NONE;
+ aic_state |= AIC_STATE_RECORDING;
+
+ rec_dma_start(addr, size);
+ jz_writef(AIC_CCR, RDMS(1), EROR(1), EREC(1));
+}
+
+void pcm_rec_dma_stop(void)
+{
+ jz_writef(AIC_CCR, RDMS(0), EROR(0), EREC(0));
+ jz_writef(AIC_CCR, RFLUSH(1));
+
+ rec_dma_pending_event = DMA_EVENT_NONE;
+ aic_state &= ~AIC_STATE_RECORDING;
+}
+
+void pcm_rec_lock(void)
+{
+ int irq = disable_irq_save();
+ ++rec_lock;
+ restore_irq(irq);
+}
+
+void pcm_rec_unlock(void)
+{
+ int irq = disable_irq_save();
+ if(--rec_lock == 0 && (aic_state & AIC_STATE_RECORDING)) {
+ rec_dma_handle_event(rec_dma_pending_event);
+ rec_dma_pending_event = DMA_EVENT_NONE;
+ }
+
+ restore_irq(irq);
+}
+
+const void* pcm_rec_dma_get_peak_buffer(void)
+{
+ return (const void*)UNCACHEDADDR(REG_DMA_CHN_TA(DMA_CHANNEL_RECORD));
+}
+#endif /* HAVE_RECORDING */
+
+#ifdef HAVE_PCM_DMA_ADDRESS
+void* pcm_dma_addr(void* addr)
+{
+ return (void*)UNCACHEDADDR(addr);
+}
+#endif
+
void AIC(void)
{
if(jz_readf(AIC_SR, TUR)) {
aic_tx_underruns += 1;
jz_writef(AIC_SR, TUR(0));
}
+
+#ifdef HAVE_RECORDING
+ if(jz_readf(AIC_SR, ROR)) {
+ aic_rx_overruns += 1;
+ jz_writef(AIC_SR, ROR(0));
+ }
+#endif /* HAVE_RECORDING */
}
diff --git a/firmware/target/mips/ingenic_x1000/pwm-x1000.c b/firmware/target/mips/ingenic_x1000/pwm-x1000.c
index 37d2856c1a..e8243a42ce 100644
--- a/firmware/target/mips/ingenic_x1000/pwm-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/pwm-x1000.c
@@ -26,14 +26,7 @@
#include "kernel.h"
#include "x1000/tcu.h"
-struct pwm_gpio_data {
- int port;
- unsigned pin;
- int func;
-};
-
struct pwm_state {
- struct pwm_gpio_data gpio;
int period_ns;
int duty_ns;
int full_ticks;
@@ -42,11 +35,27 @@ struct pwm_state {
};
static struct pwm_state pwm_state[] = {
- {{GPIO_C, 1 << 25, GPIO_DEVICE(0)}, -1, -1, -1, -1, -1},
- {{GPIO_C, 1 << 26, GPIO_DEVICE(1)}, -1, -1, -1, -1, -1},
- {{GPIO_C, 1 << 27, GPIO_DEVICE(1)}, -1, -1, -1, -1, -1},
- {{GPIO_B, 1 << 6, GPIO_DEVICE(2)}, -1, -1, -1, -1, -1},
- {{GPIO_C, 1 << 24, GPIO_DEVICE(0)}, -1, -1, -1, -1, -1},
+ {-1, -1, -1, -1, -1},
+ {-1, -1, -1, -1, -1},
+ {-1, -1, -1, -1, -1},
+ {-1, -1, -1, -1, -1},
+ {-1, -1, -1, -1, -1},
+};
+
+static const int pwm_gpio[] = {
+ GPIO_PC(25),
+ GPIO_PC(26),
+ GPIO_PC(27),
+ GPIO_PB(6),
+ GPIO_PC(24),
+};
+
+static const int pwm_gpio_func[] = {
+ GPIOF_DEVICE(0),
+ GPIOF_DEVICE(1),
+ GPIOF_DEVICE(1),
+ GPIOF_DEVICE(2),
+ GPIOF_DEVICE(0),
};
void pwm_init(int chn)
@@ -60,7 +69,7 @@ void pwm_init(int chn)
st->prescaler = -1;
/* clear GPIO and disable timer */
- gpio_config(st->gpio.port, st->gpio.pin, GPIO_OUTPUT(0));
+ gpio_set_function(pwm_gpio[chn], GPIOF_OUTPUT(0));
jz_clr(TCU_STOP, 1 << chn);
jz_clr(TCU_ENABLE, 1 << chn);
jz_set(TCU_STOP, 1 << chn);
@@ -126,11 +135,10 @@ void pwm_set_period(int chn, int period_ns, int duty_ns)
if(full_ticks != st->full_ticks || half_ticks != st->half_ticks) {
if(enabled) {
- /* avoid changing PWM settings in the middle of a cycle */
- unsigned cmp = REG_TCU_CMP_FULL(chn) - 1;
- long deadline = current_tick + 3;
- while(REG_TCU_COUNT(chn) < cmp
- && TIME_BEFORE(current_tick, deadline));
+ /* avoid changing PWM settings in the middle of a cycle,
+ * because for some reason, that is supposed to be "bad" */
+ jz_clr(TCU_FLAG, 1 << chn);
+ while((REG_TCU_FLAG & (1 << chn)) == 0);
}
/* these can be changed while the timer is running */
@@ -154,15 +162,13 @@ void pwm_enable(int chn)
jz_set(TCU_ENABLE, 1 << chn);
/* Configure GPIO function */
- struct pwm_state* st = &pwm_state[chn];
- gpio_config(st->gpio.port, st->gpio.pin, st->gpio.func);
+ gpio_set_function(pwm_gpio[chn], pwm_gpio_func[chn]);
}
void pwm_disable(int chn)
{
/* Set GPIO to output 0 */
- struct pwm_state* st = &pwm_state[chn];
- gpio_config(st->gpio.port, st->gpio.pin, GPIO_OUTPUT(0));
+ gpio_set_function(pwm_gpio[chn], GPIOF_OUTPUT(0));
/* Stop timer */
jz_clr(TCU_ENABLE, 1 << chn);
diff --git a/firmware/target/mips/ingenic_x1000/sd-x1000.c b/firmware/target/mips/ingenic_x1000/sd-x1000.c
index 7fba617ce3..679a25a222 100644
--- a/firmware/target/mips/ingenic_x1000/sd-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/sd-x1000.c
@@ -23,6 +23,7 @@
#include "sdmmc.h"
#include "sd.h"
#include "msc-x1000.h"
+#include "gpio-x1000.h"
#include <string.h>
/* #define LOGF_ENABLE */
@@ -201,7 +202,7 @@ bool sd_removable(IF_MD_NONVOID(int drive))
if(drive < 0)
return false;
- return sd_to_msc[IF_MD_DRV(drive)]->config->cd_gpio.pin != 0;
+ return sd_to_msc[IF_MD_DRV(drive)]->config->cd_gpio != GPIO_NONE;
}
#ifndef CONFIG_STORAGE_MULTI
diff --git a/firmware/target/mips/ingenic_x1000/sfc-x1000.c b/firmware/target/mips/ingenic_x1000/sfc-x1000.c
index 9537cdc035..5ade6bcc64 100644
--- a/firmware/target/mips/ingenic_x1000/sfc-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/sfc-x1000.c
@@ -21,278 +21,178 @@
#include "system.h"
#include "kernel.h"
-#include "panic.h"
#include "sfc-x1000.h"
-#include "gpio-x1000.h"
+#include "clk-x1000.h"
#include "irq-x1000.h"
-#include "x1000/sfc.h"
-#include "x1000/cpm.h"
-
-#ifndef BOOTLOADER_SPL
-/* DMA only works once the system is properly booted */
-# define NEED_SFC_DMA
-#endif
-
-#if defined(BOOTLOADER_SPL)
-# if X1000_EXCLK_FREQ == 24000000
-# define FIXED_CLK_FREQ 600000000
-# define FIXED_CLK_SRC X1000_CLK_MPLL
-# elif X1000_EXCLK_FREQ == 26000000
-# define FIXED_CLK_FREQ 598000000
-# define FIXED_CLK_SRC X1000_CLK_MPLL
-# else
-# error "bad EXCLK freq"
-# endif
-#endif
+/* #define USE_DMA */
#define FIFO_THRESH 31
-#define SFC_STATUS_PENDING (-1)
+static void sfc_poll_wait(void);
+#ifdef USE_DMA
+static void sfc_irq_wait(void);
-#ifdef NEED_SFC_DMA
-static struct mutex sfc_mutex;
static struct semaphore sfc_sema;
-static struct timeout sfc_lockup_tmo;
-static bool sfc_inited = false;
-static volatile int sfc_status;
-#else
-# define sfc_status SFC_STATUS_OK
-#endif
-void sfc_init(void)
-{
-#ifdef NEED_SFC_DMA
- if(sfc_inited)
- return;
-
- mutex_init(&sfc_mutex);
- semaphore_init(&sfc_sema, 1, 0);
- sfc_inited = true;
+/* This function pointer thing is a hack for the SPL, since it has to use
+ * the NAND driver directly and we can't afford to drag in the whole kernel
+ * just to wait on a semaphore. */
+static void(*sfc_wait)(void) = sfc_poll_wait;
#endif
-}
-
-void sfc_lock(void)
-{
-#ifdef NEED_SFC_DMA
- mutex_lock(&sfc_mutex);
-#endif
-}
-
-void sfc_unlock(void)
-{
-#ifdef NEED_SFC_DMA
- mutex_unlock(&sfc_mutex);
-#endif
-}
void sfc_open(void)
{
- gpio_config(GPIO_A, 0x3f << 26, GPIO_DEVICE(1));
jz_writef(CPM_CLKGR, SFC(0));
+#ifdef USE_DMA
+ jz_writef(SFC_GLB, OP_MODE_V(DMA), BURST_MD_V(INCR32),
+ PHASE_NUM(1), THRESHOLD(FIFO_THRESH), WP_EN(1));
+#else
jz_writef(SFC_GLB, OP_MODE_V(SLAVE), PHASE_NUM(1),
THRESHOLD(FIFO_THRESH), WP_EN(1));
+#endif
REG_SFC_CGE = 0;
REG_SFC_INTC = 0x1f;
REG_SFC_MEM_ADDR = 0;
-
-#ifdef NEED_SFC_DMA
- jz_writef(SFC_GLB, OP_MODE_V(DMA), BURST_MD_V(INCR32));
- system_enable_irq(IRQ_SFC);
-#endif
}
void sfc_close(void)
{
-#ifdef NEED_SFC_DMA
- system_disable_irq(IRQ_SFC);
-#endif
-
REG_SFC_CGE = 0x1f;
jz_writef(CPM_CLKGR, SFC(1));
}
-void sfc_set_clock(x1000_clk_t clksrc, uint32_t freq)
-{
- uint32_t in_freq;
-#ifdef FIXED_CLK_FREQ
- /* Small optimization to save code space in SPL by not polling clock */
- clksrc = FIXED_CLK_SRC;
- in_freq = FIXED_CLK_FREQ;
-#else
- in_freq = clk_get(clksrc);
-#endif
-
- uint32_t div = clk_calc_div(in_freq, freq);
- jz_writef(CPM_SSICDR, CE(1), CLKDIV(div - 1),
- SFC_CS(clksrc == X1000_CLK_MPLL ? 1 : 0));
- while(jz_readf(CPM_SSICDR, BUSY));
- jz_writef(CPM_SSICDR, CE(0));
-}
-
-#ifdef NEED_SFC_DMA
-static int sfc_lockup_tmo_cb(struct timeout* tmo)
+void sfc_irq_begin(void)
{
- (void)tmo;
-
- int irq = disable_irq_save();
- if(sfc_status == SFC_STATUS_PENDING) {
- sfc_status = SFC_STATUS_LOCKUP;
- jz_overwritef(SFC_TRIG, STOP(1));
- semaphore_release(&sfc_sema);
+#ifdef USE_DMA
+ static bool inited = false;
+ if(!inited) {
+ semaphore_init(&sfc_sema, 1, 0);
+ inited = true;
}
- restore_irq(irq);
- return 0;
+ system_enable_irq(IRQ_SFC);
+ sfc_wait = sfc_irq_wait;
+#endif
}
-static void sfc_wait_end(void)
+void sfc_irq_end(void)
{
- semaphore_wait(&sfc_sema, TIMEOUT_BLOCK);
+#ifdef USE_DMA
+ system_disable_irq(IRQ_SFC);
+ sfc_wait = sfc_poll_wait;
+#endif
}
-void SFC(void)
+void sfc_set_clock(uint32_t freq)
{
- unsigned sr = REG_SFC_SR & ~REG_SFC_INTC;
-
- if(jz_vreadf(sr, SFC_SR, OVER)) {
- jz_overwritef(SFC_SCR, CLR_OVER(1));
- sfc_status = SFC_STATUS_OVERFLOW;
- } else if(jz_vreadf(sr, SFC_SR, UNDER)) {
- jz_overwritef(SFC_SCR, CLR_UNDER(1));
- sfc_status = SFC_STATUS_UNDERFLOW;
- } else if(jz_vreadf(sr, SFC_SR, END)) {
- jz_overwritef(SFC_SCR, CLR_END(1));
- sfc_status = SFC_STATUS_OK;
- } else {
- panicf("SFC IRQ bug");
- return;
+ /* FIXME: Get rid of this hack & allow defining a real clock tree... */
+ x1000_clk_t clksrc = X1000_CLK_MPLL;
+ uint32_t in_freq = clk_get(clksrc);
+ if(in_freq < freq) {
+ clksrc = X1000_CLK_SCLK_A;
+ in_freq = clk_get(clksrc);
}
- /* Not sure this is wholly correct */
- if(sfc_status != SFC_STATUS_OK)
- jz_overwritef(SFC_TRIG, STOP(1));
-
- REG_SFC_INTC = 0x1f;
- semaphore_release(&sfc_sema);
+ uint32_t div = clk_calc_div(in_freq, freq);
+ jz_writef(CPM_SSICDR, CE(1), CLKDIV(div - 1),
+ SFC_CS(clksrc == X1000_CLK_MPLL ? 1 : 0));
+ while(jz_readf(CPM_SSICDR, BUSY));
+ jz_writef(CPM_SSICDR, CE(0));
}
-#else
-/* Note the X1000 is *very* picky about how the SFC FIFOs are accessed
- * so please do NOT try to rearrange the code without testing it first!
- */
-void sfc_fifo_read(unsigned* buffer, int data_bytes)
+#ifndef USE_DMA
+static void sfc_fifo_rdwr(bool write, void* buffer, uint32_t data_bytes)
{
- int data_words = (data_bytes + 3) / 4;
+ uint32_t* word_buf = (uint32_t*)buffer;
+ uint32_t sr_bit = write ? BM_SFC_SR_TREQ : BM_SFC_SR_RREQ;
+ uint32_t clr_bit = write ? BM_SFC_SCR_CLR_TREQ : BM_SFC_SCR_CLR_RREQ;
+ uint32_t data_words = (data_bytes + 3) / 4;
while(data_words > 0) {
- if(jz_readf(SFC_SR, RREQ)) {
- jz_overwritef(SFC_SCR, CLR_RREQ(1));
+ if(REG_SFC_SR & sr_bit) {
+ REG_SFC_SCR = clr_bit;
- int amount = data_words > FIFO_THRESH ? FIFO_THRESH : data_words;
+ /* We need to read/write in bursts equal to FIFO threshold amount
+ * X1000 PM, 10.8.5, SFC > software guidelines > slave mode */
+ uint32_t amount = MIN(data_words, FIFO_THRESH);
data_words -= amount;
- while(amount > 0) {
- *buffer++ = REG_SFC_DATA;
- amount -= 1;
- }
- }
- }
-}
-void sfc_fifo_write(const unsigned* buffer, int data_bytes)
-{
- int data_words = (data_bytes + 3) / 4;
- while(data_words > 0) {
- if(jz_readf(SFC_SR, TREQ)) {
- jz_overwritef(SFC_SCR, CLR_TREQ(1));
-
- int amount = data_words > FIFO_THRESH ? FIFO_THRESH : data_words;
- data_words -= amount;
- while(amount > 0) {
- REG_SFC_DATA = *buffer++;
- amount -= 1;
+ uint32_t* endptr = word_buf + amount;
+ for(; word_buf != endptr; ++word_buf) {
+ if(write)
+ REG_SFC_DATA = *word_buf;
+ else
+ *word_buf = REG_SFC_DATA;
}
}
}
}
-
-static void sfc_wait_end(void)
-{
- while(jz_readf(SFC_SR, END) == 0);
- jz_overwritef(SFC_SCR, CLR_TREQ(1));
-}
-
-#endif /* NEED_SFC_DMA */
-
-int sfc_exec(const sfc_op* op)
-{
-#ifdef NEED_SFC_DMA
- uint32_t intc_clear = jz_orm(SFC_INTC, MSK_END);
#endif
- if(op->flags & (SFC_FLAG_READ|SFC_FLAG_WRITE)) {
- jz_writef(SFC_TRAN_CONF(0), DATA_EN(1));
- REG_SFC_TRAN_LENGTH = op->data_bytes;
-#ifdef NEED_SFC_DMA
- REG_SFC_MEM_ADDR = PHYSADDR(op->buffer);
-#endif
-
- if(op->flags & SFC_FLAG_READ)
- {
- jz_writef(SFC_GLB, TRAN_DIR_V(READ));
-#ifdef NEED_SFC_DMA
- discard_dcache_range(op->buffer, op->data_bytes);
- intc_clear |= jz_orm(SFC_INTC, MSK_OVER);
-#endif
- }
- else
- {
- jz_writef(SFC_GLB, TRAN_DIR_V(WRITE));
-#ifdef NEED_SFC_DMA
- commit_dcache_range(op->buffer, op->data_bytes);
- intc_clear |= jz_orm(SFC_INTC, MSK_UNDER);
+void sfc_exec(uint32_t cmd, uint32_t addr, void* data, uint32_t size)
+{
+ /* Deal with transfer direction */
+ bool write = (size & SFC_WRITE) != 0;
+ uint32_t glb = REG_SFC_GLB;
+ if(data) {
+ if(write) {
+ jz_vwritef(glb, SFC_GLB, TRAN_DIR_V(WRITE));
+ size &= ~SFC_WRITE;
+#ifdef USE_DMA
+ commit_dcache_range(data, size);
+#endif
+ } else {
+ jz_vwritef(glb, SFC_GLB, TRAN_DIR_V(READ));
+#ifdef USE_DMA
+ discard_dcache_range(data, size);
#endif
}
- } else {
- jz_writef(SFC_TRAN_CONF(0), DATA_EN(0));
- REG_SFC_TRAN_LENGTH = 0;
-#ifdef NEED_SFC_DMA
- REG_SFC_MEM_ADDR = 0;
-#endif
}
- bool dummy_first = (op->flags & SFC_FLAG_DUMMYFIRST) != 0;
- jz_writef(SFC_TRAN_CONF(0),
- MODE(op->mode), POLL_EN(0),
- ADDR_WIDTH(op->addr_bytes),
- PHASE_FMT(dummy_first ? 1 : 0),
- DUMMY_BITS(op->dummy_bits),
- COMMAND(op->command), CMD_EN(1));
-
- REG_SFC_DEV_ADDR(0) = op->addr_lo;
- REG_SFC_DEV_PLUS(0) = op->addr_hi;
+ /* Program transfer configuration */
+ REG_SFC_GLB = glb;
+ REG_SFC_TRAN_LENGTH = size;
+#ifdef USE_DMA
+ REG_SFC_MEM_ADDR = PHYSADDR(data);
+#endif
+ REG_SFC_TRAN_CONF(0) = cmd;
+ REG_SFC_DEV_ADDR(0) = addr;
+ REG_SFC_DEV_PLUS(0) = 0;
-#ifdef NEED_SFC_DMA
- sfc_status = SFC_STATUS_PENDING;
- timeout_register(&sfc_lockup_tmo, sfc_lockup_tmo_cb, 10*HZ, 0);
+ /* Clear old interrupts */
REG_SFC_SCR = 0x1f;
- REG_SFC_INTC &= ~intc_clear;
-#endif
+ jz_writef(SFC_INTC, MSK_END(0));
+ /* Start the command */
jz_overwritef(SFC_TRIG, FLUSH(1));
jz_overwritef(SFC_TRIG, START(1));
-#ifndef NEED_SFC_DMA
- if(op->flags & SFC_FLAG_READ)
- sfc_fifo_read((unsigned*)op->buffer, op->data_bytes);
- if(op->flags & SFC_FLAG_WRITE)
- sfc_fifo_write((const unsigned*)op->buffer, op->data_bytes);
+ /* Data transfer by PIO or DMA, and wait for completion */
+#ifndef USE_DMA
+ sfc_fifo_rdwr(write, data, size);
+ sfc_poll_wait();
+#else
+ sfc_wait();
#endif
+}
- sfc_wait_end();
+static void sfc_poll_wait(void)
+{
+ while(jz_readf(SFC_SR, END) == 0);
+ jz_overwritef(SFC_SCR, CLR_END(1));
+}
-#ifdef NEED_SFC_DMA
- if(op->flags & SFC_FLAG_READ)
- discard_dcache_range(op->buffer, op->data_bytes);
-#endif
+#ifdef USE_DMA
+static void sfc_irq_wait(void)
+{
+ semaphore_wait(&sfc_sema, TIMEOUT_BLOCK);
+}
- return sfc_status;
+void SFC(void)
+{
+ /* the only interrupt we use is END; errors are basically not
+ * possible with the SPI interface... */
+ semaphore_release(&sfc_sema);
+ jz_overwritef(SFC_SCR, CLR_END(1));
+ jz_writef(SFC_INTC, MSK_END(1));
}
+#endif
diff --git a/firmware/target/mips/ingenic_x1000/sfc-x1000.h b/firmware/target/mips/ingenic_x1000/sfc-x1000.h
index 283f171697..afb4aa3ce6 100644
--- a/firmware/target/mips/ingenic_x1000/sfc-x1000.h
+++ b/firmware/target/mips/ingenic_x1000/sfc-x1000.h
@@ -19,87 +19,107 @@
*
****************************************************************************/
+#ifndef __SFC_X1000_H__
+#define __SFC_X1000_H__
+
+#include "x1000/sfc.h"
#include <stdint.h>
#include <stdbool.h>
-#include "clk-x1000.h"
-#include "x1000/sfc.h"
-/* SPI flash controller interface -- this is a low-level driver upon which
- * you can build NAND/NOR flash drivers. The main function is sfc_exec(),
- * used to issue commands, transfer data, etc.
+/* SPI transfer mode. SFC_TMODE_X_Y_Z means:
+ *
+ * - X lines for command phase
+ * - Y lines for address+dummy phase
+ * - Z lines for data phase
+ */
+#define SFC_TMODE_1_1_1 0
+#define SFC_TMODE_1_1_2 1
+#define SFC_TMODE_1_2_2 2
+#define SFC_TMODE_2_2_2 3
+#define SFC_TMODE_1_1_4 4
+#define SFC_TMODE_1_4_4 5
+#define SFC_TMODE_4_4_4 6
+
+/* Phase format
+ * _____________________
+ * / SFC_PFMT_ADDR_FIRST \
+ * +-----+-------+-------+------+
+ * | cmd | addr | dummy | data |
+ * +-----+-------+-------+------+
+ * ______________________
+ * / SFC_PFMT_DUMMY_FIRST \
+ * +-----+-------+-------+------+
+ * | cmd | dummy | addr | data |
+ * +-----+-------+-------+------+
*/
+#define SFC_PFMT_ADDR_FIRST 0
+#define SFC_PFMT_DUMMY_FIRST 1
-#define SFC_FLAG_READ 0x01 /* Read data */
-#define SFC_FLAG_WRITE 0x02 /* Write data */
-#define SFC_FLAG_DUMMYFIRST 0x04 /* Do dummy bits before sending address.
- * Default is dummy bits after address.
- */
+/* Direction of transfer flag */
+#define SFC_READ 0
+#define SFC_WRITE (1 << 31)
-/* SPI transfer mode. If in doubt, check with the X1000 manual and confirm
- * the transfer format is what you expect.
+/** \brief Macro to generate an SFC command for use with sfc_exec()
+ * \param cmd Command number (up to 16 bits)
+ * \param tmode SPI transfer mode
+ * \param awidth Number of address bytes
+ * \param dwidth Number of dummy cycles (1 cycle = 1 bit)
+ * \param pfmt Phase format (address first or dummy first)
+ * \param data_en 1 to enable data phase, 0 to omit it
*/
-#define SFC_MODE_STANDARD 0
-#define SFC_MODE_DUAL_IN_DUAL_OUT 1
-#define SFC_MODE_DUAL_IO 2
-#define SFC_MODE_FULL_DUAL_IO 3
-#define SFC_MODE_QUAD_IN_QUAD_OUT 4
-#define SFC_MODE_QUAD_IO 5
-#define SFC_MODE_FULL_QUAD_IO 6
+#define SFC_CMD(cmd, tmode, awidth, dwidth, pfmt, data_en) \
+ jz_orf(SFC_TRAN_CONF, COMMAND(cmd), CMD_EN(1), \
+ MODE(tmode), ADDR_WIDTH(awidth), DUMMY_BITS(dwidth), \
+ PHASE_FMT(pfmt), DATA_EN(data_en))
-/* Return status codes for sfc_exec() */
-#define SFC_STATUS_OK 0
-#define SFC_STATUS_OVERFLOW 1
-#define SFC_STATUS_UNDERFLOW 2
-#define SFC_STATUS_LOCKUP 3
+/* Open/close SFC hardware */
+extern void sfc_open(void);
+extern void sfc_close(void);
-typedef struct sfc_op {
- int command; /* Command number */
- int mode; /* SPI transfer mode */
- int flags; /* Flags for this op */
- int addr_bytes; /* Number of address bytes */
- int dummy_bits; /* Number of dummy bits (yes: bits, not bytes) */
- uint32_t addr_lo; /* Lower 32 bits of address */
- uint32_t addr_hi; /* Upper 32 bits of address */
- int data_bytes; /* Number of data bytes to read/write */
- void* buffer; /* Data buffer -- MUST be word-aligned */
-} sfc_op;
+/* Enable IRQ mode, instead of busy waiting for operations to complete.
+ * Needs to be called separately after sfc_open(), because the SPL has to
+ * use busy waiting, but we cannot #ifdef it for the SPL due to limitations
+ * of the build system. */
+extern void sfc_irq_begin(void);
+extern void sfc_irq_end(void);
-/* One-time driver init for mutexes/etc needed for handling interrupts.
- * This can be safely called multiple times; only the first call will
- * actually perform the init.
- */
-extern void sfc_init(void);
+/* Change the SFC clock frequency */
+extern void sfc_set_clock(uint32_t freq);
+
+/* Set the device configuration register */
+static inline void sfc_set_dev_conf(uint32_t conf)
+{
+ REG_SFC_DEV_CONF = conf;
+}
-/* Controller mutex -- lock before touching the driver */
-extern void sfc_lock(void);
-extern void sfc_unlock(void);
+/* Control the state of the write protect pin */
+static inline void sfc_set_wp_enable(bool en)
+{
+ jz_writef(SFC_GLB, WP_EN(en ? 1 : 0));
+}
-/* Open/close the driver. The driver must be open in order to do operations.
- * Closing the driver shuts off the hardware; the driver can be re-opened at
- * a later time when it's needed again.
+/** \brief Execute a command
+ * \param cmd Command encoded by `SFC_CMD` macro.
+ * \param addr Address up to 32 bits; pass 0 if the command doesn't need it
+ * \param data Buffer for data transfer commands, must be cache-aligned
+ * \param size Number of data bytes / direction of transfer flag
+ * \returns SFC status code: 0 on success and < 0 on failure.
*
- * After opening the driver, you must also program a valid device configuration
- * and clock rate using sfc_set_dev_conf() and sfc_set_clock().
+ * - Non-data commands must pass `data = NULL` and `size = 0` in order to
+ * get correct results.
+ *
+ * - Data commands must specify a direction of transfer using the high bit
+ * of the `size` argument by OR'ing in `SFC_READ` or `SFC_WRITE`.
*/
-extern void sfc_open(void);
-extern void sfc_close(void);
+extern void sfc_exec(uint32_t cmd, uint32_t addr, void* data, uint32_t size);
-/* These functions can be called at any time while the driver is open, but
- * must not be called while there is an operation in progress. It's the
- * caller's job to ensure the configuration will work with the device and
- * be capable of reading back data correctly.
+/* NOTE: the above will need to be changed if we need better performance
+ * The hardware can do multiple commands in a sequence, including polling,
+ * and emit an interrupt only at the end.
*
- * - sfc_set_dev_conf() writes its argument to the SFC_DEV_CONF register.
- * - sfc_set_wp_enable() sets the state of the write-protect pin (WP).
- * - sfc_set_clock() sets the controller clock frequency (in Hz).
+ * Also, some chips need more than 4 address bytes even though the block
+ * and page numbers would still fit in 32 bits; the current API cannot
+ * handle this.
*/
-#define sfc_set_dev_conf(dev_conf) \
- do { REG_SFC_DEV_CONF = (dev_conf); } while(0)
-
-#define sfc_set_wp_enable(en) \
- jz_writef(SFC_GLB, WP_EN((en) ? 1 : 0))
-
-extern void sfc_set_clock(x1000_clk_t clksrc, uint32_t freq);
-/* Execute an operation. Returns zero on success, nonzero on failure. */
-extern int sfc_exec(const sfc_op* op);
+#endif /* __SFC_X1000_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/adc-target.h b/firmware/target/mips/ingenic_x1000/shanlingq1/adc-target.h
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/shanlingq1/adc-target.h
diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/audiohw-shanlingq1.c b/firmware/target/mips/ingenic_x1000/shanlingq1/audiohw-shanlingq1.c
new file mode 100644
index 0000000000..787c35c494
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/shanlingq1/audiohw-shanlingq1.c
@@ -0,0 +1,191 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 "audiohw.h"
+#include "system.h"
+#include "pcm_sampr.h"
+#include "aic-x1000.h"
+#include "i2c-x1000.h"
+#include "gpio-x1000.h"
+#include "x1000/aic.h"
+#include "x1000/cpm.h"
+
+/* Codec has an dedicated oscillator connected, so it can operate
+ * as i2s master or slave. I can't distinguish any difference in
+ * terms of audio quality or power consumption. Code is left here
+ * for reference in case it proves useful to change it. */
+#define CODEC_MASTER_MODE 0
+
+static int cur_fsel = HW_FREQ_48;
+static int cur_vol_l = 0, cur_vol_r = 0;
+static int cur_filter = 0;
+static enum es9218_amp_mode cur_amp_mode = ES9218_AMP_MODE_1VRMS;
+
+static void codec_start(void)
+{
+ es9218_open();
+ es9218_mute(true);
+ es9218_set_iface_role(CODEC_MASTER_MODE ? ES9218_IFACE_ROLE_MASTER
+ : ES9218_IFACE_ROLE_SLAVE);
+ es9218_set_iface_format(ES9218_IFACE_FORMAT_I2S, ES9218_IFACE_BITS_32);
+ es9218_set_dpll_bandwidth(10);
+ es9218_set_thd_compensation(true);
+ es9218_set_thd_coeffs(0, 0);
+ audiohw_set_filter_roll_off(cur_filter);
+ audiohw_set_frequency(cur_fsel);
+ audiohw_set_volume(cur_vol_l, cur_vol_r);
+ es9218_set_amp_mode(cur_amp_mode);
+}
+
+static void codec_stop(void)
+{
+ es9218_mute(true);
+ es9218_close();
+ mdelay(4);
+}
+
+void audiohw_init(void)
+{
+ /* Configure AIC */
+ aic_set_external_codec(true);
+ aic_set_i2s_mode(CODEC_MASTER_MODE ? AIC_I2S_SLAVE_MODE
+ : AIC_I2S_MASTER_MODE);
+ aic_enable_i2s_bit_clock(true);
+
+ /* Open DAC driver */
+ i2c_x1000_set_freq(1, I2C_FREQ_400K);
+ codec_start();
+}
+
+void audiohw_postinit(void)
+{
+ es9218_mute(false);
+}
+
+void audiohw_close(void)
+{
+ codec_stop();
+}
+
+void audiohw_set_frequency(int fsel)
+{
+ int sampr = hw_freq_sampr[fsel];
+
+ /* choose clock gear setting, in line with the OF */
+ enum es9218_clock_gear clkgear;
+ if(sampr <= 48000)
+ clkgear = ES9218_CLK_GEAR_4;
+ else if(sampr <= 96000)
+ clkgear = ES9218_CLK_GEAR_2;
+ else
+ clkgear = ES9218_CLK_GEAR_1;
+
+ aic_enable_i2s_bit_clock(false);
+ es9218_set_clock_gear(clkgear);
+
+ if(CODEC_MASTER_MODE)
+ es9218_set_nco_frequency(sampr);
+ else
+ aic_set_i2s_clock(X1000_CLK_SCLK_A, sampr, 64);
+
+ aic_enable_i2s_bit_clock(true);
+
+ /* save frequency selection */
+ cur_fsel = fsel;
+}
+
+static int round_step_up(int x, int step)
+{
+ int rem = x % step;
+ if(rem > 0)
+ rem -= step;
+ return x - rem;
+}
+
+void audiohw_set_volume(int vol_l, int vol_r)
+{
+ /* save volume */
+ cur_vol_l = vol_l;
+ cur_vol_r = vol_r;
+
+ /* adjust the amp setting first */
+ int amp = round_step_up(MAX(vol_l, vol_r), ES9218_AMP_VOLUME_STEP);
+ amp = MIN(amp, ES9218_AMP_VOLUME_MAX);
+ amp = MAX(amp, ES9218_AMP_VOLUME_MIN);
+
+ /* adjust digital volumes */
+ vol_l -= amp;
+ vol_l = MIN(vol_l, ES9218_DIG_VOLUME_MAX);
+ vol_l = MAX(vol_l, ES9218_DIG_VOLUME_MIN);
+
+ vol_r -= amp;
+ vol_r = MIN(vol_r, ES9218_DIG_VOLUME_MAX);
+ vol_r = MAX(vol_r, ES9218_DIG_VOLUME_MIN);
+
+ /* program DAC */
+ es9218_set_amp_volume(amp);
+ es9218_set_dig_volume(vol_l, vol_r);
+}
+
+void audiohw_set_filter_roll_off(int value)
+{
+ cur_filter = value;
+ es9218_set_filter(value);
+}
+
+void audiohw_set_power_mode(int mode)
+{
+ enum es9218_amp_mode new_amp_mode;
+ if(mode == SOUND_HIGH_POWER)
+ new_amp_mode = ES9218_AMP_MODE_2VRMS;
+ else
+ new_amp_mode = ES9218_AMP_MODE_1VRMS;
+
+ if(new_amp_mode != cur_amp_mode) {
+ codec_stop();
+ cur_amp_mode = new_amp_mode;
+ codec_start();
+ es9218_mute(false);
+ }
+}
+
+void es9218_set_power_pin(int level)
+{
+ gpio_set_level(GPIO_ES9218_POWER, level ? 1 : 0);
+}
+
+void es9218_set_reset_pin(int level)
+{
+ gpio_set_level(GPIO_ES9218_RESET, level ? 1 : 0);
+}
+
+uint32_t es9218_get_mclk(void)
+{
+ /* Measured by running the DAC in asynchronous I2S slave mode,
+ * and reading back the DPLL number from regs 0x42-0x45 while
+ * playing back 44.1 KHz audio.
+ *
+ * CLK = (44_100 * 2**32) / 0x4b46e5
+ * = 38_393_403.29532737
+ * ~ 38.4 Mhz
+ */
+ return 38400000;
+}
diff --git a/firmware/target/arm/pnx0101/iriver-ifp7xx/backlight-ifp7xx.c b/firmware/target/mips/ingenic_x1000/shanlingq1/backlight-shanlingq1.c
index 8be7a59de2..32c1b902aa 100644
--- a/firmware/target/arm/pnx0101/iriver-ifp7xx/backlight-ifp7xx.c
+++ b/firmware/target/mips/ingenic_x1000/shanlingq1/backlight-shanlingq1.c
@@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Copyright (C) 2006 by Barry Wardell
+ * Copyright (C) 2021 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -18,18 +18,46 @@
* KIND, either express or implied.
*
****************************************************************************/
-#include "config.h"
-#include "cpu.h"
-#include "system.h"
+
#include "backlight.h"
+#include "backlight-target.h"
#include "lcd.h"
+#include "pwm-x1000.h"
+
+#define BL_LCD_CHN 0
+#define BL_LCD_PERIOD 10000
+
+static int backlight_calc_duty(int period, int min_duty, int brightness)
+{
+ return min_duty + (period - min_duty) * brightness / MAX_BRIGHTNESS_SETTING;
+}
+
+bool backlight_hw_init(void)
+{
+ pwm_init(BL_LCD_CHN);
+ pwm_enable(BL_LCD_CHN);
+ backlight_hw_brightness(MAX_BRIGHTNESS_SETTING);
+ return true;
+}
void backlight_hw_on(void)
{
- GPIO3_SET = 1;
+ pwm_enable(BL_LCD_CHN);
+#ifdef HAVE_LCD_ENABLE
+ lcd_enable(true);
+#endif
}
void backlight_hw_off(void)
{
- GPIO3_CLR = 1;
+ pwm_disable(BL_LCD_CHN);
+#ifdef HAVE_LCD_ENABLE
+ lcd_enable(false);
+#endif
+}
+
+void backlight_hw_brightness(int brightness)
+{
+ int duty_ns = backlight_calc_duty(BL_LCD_PERIOD, 0, brightness);
+ pwm_set_period(BL_LCD_CHN, BL_LCD_PERIOD, duty_ns);
}
diff --git a/firmware/export/installer.h b/firmware/target/mips/ingenic_x1000/shanlingq1/backlight-target.h
index 802798618d..7298c1c06a 100644
--- a/firmware/export/installer.h
+++ b/firmware/target/mips/ingenic_x1000/shanlingq1/backlight-target.h
@@ -19,13 +19,15 @@
*
****************************************************************************/
-#ifndef _INSTALLER_H_
-#define _INSTALLER_H_
+#ifndef __BACKLIGHT_TARGET_H__
+#define __BACKLIGHT_TARGET_H__
-/* Provisional interface for installing/dumping a bootloader */
+#include <stdbool.h>
-extern int install_bootloader(const char* path);
-extern int dump_bootloader(const char* path);
-extern const char* installer_strerror(int rc);
+extern bool backlight_hw_init(void);
-#endif /* _INSTALLER_H_ */
+extern void backlight_hw_on(void);
+extern void backlight_hw_off(void);
+extern void backlight_hw_brightness(int brightness);
+
+#endif /* __BACKLIGHT_TARGET_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/button-shanlingq1.c b/firmware/target/mips/ingenic_x1000/shanlingq1/button-shanlingq1.c
new file mode 100644
index 0000000000..13b0cdd078
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/shanlingq1/button-shanlingq1.c
@@ -0,0 +1,258 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ * Copyright (C) 2021 Dana Conrad
+ *
+ * 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 "button.h"
+#include "touchscreen.h"
+#include "ft6x06.h"
+#include "axp-pmu.h"
+#include "kernel.h"
+#include "backlight.h"
+#include "powermgmt.h"
+#include "gpio-x1000.h"
+#include "irq-x1000.h"
+#include "i2c-x1000.h"
+#include <stdbool.h>
+
+#ifndef BOOTLOADER
+# include "lcd.h"
+# include "font.h"
+#endif
+
+/* Volume wheel rotation */
+static volatile int wheel_pos = 0;
+
+/* Value of headphone detect register */
+static uint8_t hp_detect_reg = 0x00;
+
+/* Interval to poll the register */
+#define HPD_POLL_TIME (HZ/2)
+
+static int hp_detect_tmo_cb(struct timeout* tmo)
+{
+ i2c_descriptor* d = (i2c_descriptor*)tmo->data;
+ i2c_async_queue(AXP_PMU_BUS, TIMEOUT_NOBLOCK, I2C_Q_ADD, 0, d);
+ return HPD_POLL_TIME;
+}
+
+static void hp_detect_init(void)
+{
+ /* TODO: replace this copy paste cruft with an API in axp-pmu */
+ static struct timeout tmo;
+ static const uint8_t gpio_reg = AXP192_REG_GPIOSTATE1;
+ static i2c_descriptor desc = {
+ .slave_addr = AXP_PMU_ADDR,
+ .bus_cond = I2C_START | I2C_STOP,
+ .tran_mode = I2C_READ,
+ .buffer[0] = (void*)&gpio_reg,
+ .count[0] = 1,
+ .buffer[1] = &hp_detect_reg,
+ .count[1] = 1,
+ .callback = NULL,
+ .arg = 0,
+ .next = NULL,
+ };
+
+ /* Headphone detect is wired to AXP192 GPIO: set it to input state */
+ i2c_reg_write1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP192_REG_GPIO1FUNCTION, 0x01);
+
+ /* Get an initial reading before startup */
+ int r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, gpio_reg);
+ if(r >= 0)
+ hp_detect_reg = r;
+
+ /* Poll the register every second */
+ timeout_register(&tmo, &hp_detect_tmo_cb, HPD_POLL_TIME, (intptr_t)&desc);
+}
+
+void button_init_device(void)
+{
+ /* Setup interrupts for the volume wheel */
+ gpio_set_function(GPIO_WHEEL1, GPIOF_IRQ_EDGE(0));
+ gpio_set_function(GPIO_WHEEL2, GPIOF_IRQ_EDGE(0));
+ gpio_flip_edge_irq(GPIO_WHEEL1);
+ gpio_flip_edge_irq(GPIO_WHEEL2);
+ gpio_enable_irq(GPIO_WHEEL1);
+ gpio_enable_irq(GPIO_WHEEL2);
+
+ /* Init touchscreen driver */
+ i2c_x1000_set_freq(FT6x06_BUS, I2C_FREQ_400K);
+ ft6x06_init();
+
+ /* Reset touch controller */
+ gpio_set_level(GPIO_FT6x06_POWER, 1);
+ gpio_set_level(GPIO_FT6x06_RESET, 0);
+ mdelay(5);
+ gpio_set_level(GPIO_FT6x06_RESET, 1);
+
+ /* Enable ft6x06 interrupt */
+ system_set_irq_handler(GPIO_TO_IRQ(GPIO_FT6x06_INTERRUPT), ft6x06_irq_handler);
+ gpio_set_function(GPIO_FT6x06_INTERRUPT, GPIOF_IRQ_EDGE(0));
+ gpio_enable_irq(GPIO_FT6x06_INTERRUPT);
+
+ /* Headphone detection */
+ hp_detect_init();
+}
+
+int button_read_device(int* data)
+{
+ const struct ft6x06_point* point;
+ int r = 0;
+
+ /* Read GPIO buttons, these are all active low */
+ uint32_t b = REG_GPIO_PIN(GPIO_B);
+ if((b & (1 << 21)) == 0) r |= BUTTON_PREV;
+ if((b & (1 << 22)) == 0) r |= BUTTON_NEXT;
+ if((b & (1 << 28)) == 0) r |= BUTTON_PLAY;
+ if((b & (1 << 31)) == 0) r |= BUTTON_POWER;
+
+ /* Check the wheel */
+ int wheel_btn = 0;
+ int whpos = wheel_pos;
+ if(whpos > 3)
+ wheel_btn = BUTTON_VOL_DOWN;
+ else if(whpos < -3)
+ wheel_btn = BUTTON_VOL_UP;
+
+ if(wheel_btn) {
+ wheel_pos = 0;
+
+ /* Post the event (rapid motion is more reliable this way) */
+ queue_post(&button_queue, wheel_btn, 0);
+ queue_post(&button_queue, wheel_btn|BUTTON_REL, 0);
+
+ /* Poke the backlight */
+ backlight_on();
+ reset_poweroff_timer();
+ }
+
+ if(touchscreen_get_mode() == TOUCHSCREEN_POINT) {
+ /* Pointing mode can't use multitouch since we can only pass
+ * along coordinates for one touch event at a time */
+ point = &ft6x06_state.points[0];
+ int t = touchscreen_to_pixels(point->pos_x, point->pos_y, data);
+ if(point->event == FT6x06_EVT_PRESS ||
+ point->event == FT6x06_EVT_CONTACT)
+ r |= t;
+ } else {
+ /* 3x3 mode can have simultaneous 'button' presses via multitouch */
+ for(int i = 0; i < ft6x06_state.nr_points; ++i) {
+ point = &ft6x06_state.points[i];
+ if(point->event == FT6x06_EVT_PRESS ||
+ point->event == FT6x06_EVT_CONTACT)
+ r |= touchscreen_to_pixels(point->pos_x, point->pos_y, NULL);
+ }
+ }
+
+ return r;
+}
+
+void touchscreen_enable_device(bool en)
+{
+ ft6x06_enable(en);
+ /* TODO: check if it's worth shutting off the power pin */
+}
+
+bool headphones_inserted(void)
+{
+ /* TODO: Also check if the headset button is detectable via an ADC.
+ * The AXP driver should probably get proper interrupt handling,
+ * that would be useful for more things than just GPIO polling. */
+ return hp_detect_reg & 0x20 ? true : false;
+}
+
+static void handle_wheel_irq(void)
+{
+ /* Wheel stuff adapted from button-erosqnative.c */
+ static const int delta[16] = { 0, -1, 1, 0,
+ 1, 0, 0, -1,
+ -1, 0, 0, 1,
+ 0, 1, -1, 0 };
+ static uint32_t state = 0;
+ state <<= 2;
+ state |= (REG_GPIO_PIN(GPIO_D) >> 2) & 3;
+ state &= 0xf;
+
+ wheel_pos += delta[state];
+}
+
+void GPIOD02(void)
+{
+ handle_wheel_irq();
+ gpio_flip_edge_irq(GPIO_WHEEL1);
+}
+
+void GPIOD03(void)
+{
+ handle_wheel_irq();
+ gpio_flip_edge_irq(GPIO_WHEEL2);
+}
+
+#ifndef BOOTLOADER
+static int getbtn(void)
+{
+ int btn;
+ do {
+ btn = button_get_w_tmo(1);
+ } while(btn & (BUTTON_REL|BUTTON_REPEAT));
+ return btn;
+}
+
+bool dbg_shanlingq1_touchscreen(void)
+{
+ /* definition of box used to represent the touchpad */
+ const int pad_w = LCD_WIDTH;
+ const int pad_h = LCD_HEIGHT;
+ const int box_h = pad_h - SYSFONT_HEIGHT*5;
+ const int box_w = pad_w * box_h / pad_h;
+ const int box_x = (LCD_WIDTH - box_w) / 2;
+ const int box_y = SYSFONT_HEIGHT * 9 / 2;
+
+ bool draw_border = true;
+
+ do {
+ int line = 0;
+ lcd_clear_display();
+ lcd_putsf(0, line++, "nr_points: %d gesture: %d",
+ ft6x06_state.nr_points, ft6x06_state.gesture);
+
+ /* draw touchpad box borders */
+ if(draw_border)
+ lcd_drawrect(box_x, box_y, box_w, box_h);
+
+ for(int i = 0; i < ft6x06_state.nr_points; ++i) {
+ const struct ft6x06_point* point = &ft6x06_state.points[i];
+ lcd_putsf(0, line++, "pt%d id:%d pos: %d,%d wgt: %d area:%d",
+ i, point->touch_id, point->pos_x, point->pos_y,
+ point->weight, point->area);
+
+ /* draw crosshair */
+ int tx = box_x + point->pos_x * box_w / pad_w;
+ int ty = box_y + point->pos_y * box_h / pad_h;
+ lcd_hline(tx-2, tx+2, ty);
+ lcd_vline(tx, ty-2, ty+2);
+ }
+
+ lcd_update();
+ } while(getbtn() != BUTTON_POWER);
+ return false;
+}
+#endif
diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/button-target.h b/firmware/target/mips/ingenic_x1000/shanlingq1/button-target.h
new file mode 100644
index 0000000000..905d148afa
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/shanlingq1/button-target.h
@@ -0,0 +1,56 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef __BUTTON_TARGET_H__
+#define __BUTTON_TARGET_H__
+
+#include <stdbool.h>
+
+/* physical buttons */
+#define BUTTON_POWER 0x00000001
+#define BUTTON_VOL_UP 0x00000002 /* up = wheel clockwise */
+#define BUTTON_VOL_DOWN 0x00000004
+#define BUTTON_PLAY 0x00000008 /* circle */
+#define BUTTON_NEXT 0x00000010 /* down */
+#define BUTTON_PREV 0x00000020 /* up */
+
+/* compatibility hacks */
+#define BUTTON_LEFT BUTTON_MIDLEFT
+#define BUTTON_RIGHT BUTTON_MIDRIGHT
+
+/* touchscreen "buttons" */
+#define BUTTON_TOPLEFT 0x00000040
+#define BUTTON_TOPMIDDLE 0x00000080
+#define BUTTON_TOPRIGHT 0x00000100
+#define BUTTON_MIDLEFT 0x00000200
+#define BUTTON_CENTER 0x00000400
+#define BUTTON_MIDRIGHT 0x00000800
+#define BUTTON_BOTTOMLEFT 0x00001000
+#define BUTTON_BOTTOMMIDDLE 0x00002000
+#define BUTTON_BOTTOMRIGHT 0x00004000
+
+#define BUTTON_MAIN (BUTTON_POWER|BUTTON_VOL_UP|BUTTON_VOL_DOWN|\
+ BUTTON_PLAY|BUTTON_NEXT|BUTTON_PREV)
+
+#define POWEROFF_BUTTON BUTTON_POWER
+#define POWEROFF_COUNT 30
+
+#endif /* __BUTTON_TARGET_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/gpio-target.h b/firmware/target/mips/ingenic_x1000/shanlingq1/gpio-target.h
new file mode 100644
index 0000000000..7c71d12888
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/shanlingq1/gpio-target.h
@@ -0,0 +1,32 @@
+/* Name Port Pins Function */
+DEFINE_PINGROUP(LCD_DATA, GPIO_A, 0xffff << 0, GPIOF_DEVICE(1))
+DEFINE_PINGROUP(LCD_CONTROL, GPIO_B, 0x1a << 16, GPIOF_DEVICE(1))
+DEFINE_PINGROUP(MSC0, GPIO_A, 0x3f << 20, GPIOF_DEVICE(1))
+DEFINE_PINGROUP(SFC, GPIO_A, 0x3f << 26, GPIOF_DEVICE(1))
+DEFINE_PINGROUP(I2S, GPIO_B, 0x1f << 0, GPIOF_DEVICE(1))
+DEFINE_PINGROUP(I2C0, GPIO_B, 3 << 23, GPIOF_DEVICE(0))
+DEFINE_PINGROUP(I2C1, GPIO_C, 3 << 26, GPIOF_DEVICE(0))
+DEFINE_PINGROUP(I2C2, GPIO_D, 3 << 0, GPIOF_DEVICE(1))
+
+/* Name Pin Function */
+DEFINE_GPIO(FT6x06_INTERRUPT, GPIO_PA(16), GPIOF_INPUT)
+DEFINE_GPIO(USB_DETECT, GPIO_PA(17), GPIOF_INPUT)
+DEFINE_GPIO(FT6x06_RESET, GPIO_PA(19), GPIOF_OUTPUT(0))
+DEFINE_GPIO(LCD_PWR, GPIO_PB(6), GPIOF_OUTPUT(1))
+DEFINE_GPIO(FT6x06_POWER, GPIO_PB(8), GPIOF_OUTPUT(0))
+DEFINE_GPIO(MSC0_CD, GPIO_PB(9), GPIOF_INPUT)
+DEFINE_GPIO(ES9218_POWER, GPIO_PB(13), GPIOF_OUTPUT(0))
+DEFINE_GPIO(LCD_RST, GPIO_PB(15), GPIOF_OUTPUT(1))
+DEFINE_GPIO(LCD_RD, GPIO_PB(16), GPIOF_OUTPUT(1))
+DEFINE_GPIO(LCD_CE, GPIO_PB(18), GPIOF_OUTPUT(1))
+DEFINE_GPIO(BTN_PREV, GPIO_PB(21), GPIOF_INPUT)
+DEFINE_GPIO(BTN_NEXT, GPIO_PB(22), GPIOF_INPUT)
+DEFINE_GPIO(USB_DRVVBUS, GPIO_PB(25), GPIOF_OUTPUT(0))
+DEFINE_GPIO(BTN_PLAY, GPIO_PB(28), GPIOF_INPUT)
+DEFINE_GPIO(BTN_POWER, GPIO_PB(31), GPIOF_INPUT)
+DEFINE_GPIO(AXP_IRQ, GPIO_PC(21), GPIOF_INPUT)
+DEFINE_GPIO(USB_ID, GPIO_PC(23), GPIOF_INPUT)
+DEFINE_GPIO(WHEEL1, GPIO_PD(2), GPIOF_INPUT)
+DEFINE_GPIO(WHEEL2, GPIO_PD(3), GPIOF_INPUT)
+DEFINE_GPIO(ES9218_GPIO2, GPIO_PD(4), GPIOF_OUTPUT(0))
+DEFINE_GPIO(ES9218_RESET, GPIO_PD(5), GPIOF_OUTPUT(0))
diff --git a/firmware/target/arm/tcc77x/logikdax/backlight-target.h b/firmware/target/mips/ingenic_x1000/shanlingq1/i2c-target.h
index 202e118606..af19aeb28c 100644
--- a/firmware/target/arm/tcc77x/logikdax/backlight-target.h
+++ b/firmware/target/mips/ingenic_x1000/shanlingq1/i2c-target.h
@@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Copyright (C) 2007 by Dave Chapman
+ * Copyright (C) 2021 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -18,23 +18,23 @@
* KIND, either express or implied.
*
****************************************************************************/
-#ifndef BACKLIGHT_TARGET_H
-#define BACKLIGHT_TARGET_H
-#include "tcc77x.h"
+#ifndef __I2C_TARGET_H__
+#define __I2C_TARGET_H__
-#define backlight_hw_init() true
+#define I2C_ASYNC_BUS_COUNT 3
+#define I2C_ASYNC_QUEUE_SIZE 4
-static inline void backlight_hw_on(void)
-{
- /* Enable backlight */
- GPIOD |= 0x10;
-}
+#define FT6x06_BUS 0
+#define FT6x06_ADDR 0x38
-static inline void backlight_hw_off(void)
-{
- /* Disable backlight */
- GPIOD &= ~0x10;
-}
+#define ES9218_BUS 1
+#define ES9218_ADDR 0x48
-#endif
+#define AXP_PMU_BUS 2
+#define AXP_PMU_ADDR 0x34
+
+#define CW2015_BUS 2
+#define CW2015_ADDR 0x62
+
+#endif /* __I2C_TARGET_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/lcd-shanlingq1.c b/firmware/target/mips/ingenic_x1000/shanlingq1/lcd-shanlingq1.c
new file mode 100644
index 0000000000..532a149185
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/shanlingq1/lcd-shanlingq1.c
@@ -0,0 +1,399 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 "lcd.h"
+#include "system.h"
+#include "lcd-x1000.h"
+#include "gpio-x1000.h"
+
+/* LCD controller is probably an RM68090.
+ */
+
+static const uint32_t q1_lcd_cmd_enable[] = {
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0xbe,
+ LCD_INSTR_DAT, 0xc3,
+ LCD_INSTR_DAT, 0x29,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x01,
+ LCD_INSTR_DAT, 0x01,
+ LCD_INSTR_DAT, 0x04,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x02,
+ LCD_INSTR_DAT, 0x01,
+ LCD_INSTR_DAT, 0x00,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x03,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x10,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x05,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x00,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x06,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x00,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x07,
+ LCD_INSTR_DAT, 0x01,
+ LCD_INSTR_DAT, 0x03,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x08,
+ LCD_INSTR_DAT, 0x03,
+ LCD_INSTR_DAT, 0x03,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x0d,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x00,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x10,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0xc1,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x11,
+ LCD_INSTR_DAT, 0xb1,
+ LCD_INSTR_DAT, 0x08,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x12,
+ LCD_INSTR_DAT, 0xb1,
+ LCD_INSTR_DAT, 0x08,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x13,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x0f,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x14,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x14,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x15,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x04,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x16,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x00,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x22,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x00,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x23,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x00,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x30,
+ LCD_INSTR_DAT, 0x7c,
+ LCD_INSTR_DAT, 0x3f,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x32,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x00,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x70,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x01,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x91,
+ LCD_INSTR_DAT, 0x01,
+ LCD_INSTR_DAT, 0x00,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0xe0,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x01,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0xe1,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x61,
+
+ LCD_INSTR_CMD, 0x01,
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_DAT, 0x10,
+ LCD_INSTR_DAT, 0x30,
+
+ LCD_INSTR_CMD, 0x01,
+ LCD_INSTR_CMD, 0x01,
+ LCD_INSTR_DAT, 0xf6,
+ LCD_INSTR_DAT, 0x3f,
+
+ LCD_INSTR_CMD, 0x01,
+ LCD_INSTR_CMD, 0x02,
+ LCD_INSTR_DAT, 0x50,
+ LCD_INSTR_DAT, 0x1f,
+
+ LCD_INSTR_CMD, 0x01,
+ LCD_INSTR_CMD, 0x03,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x30,
+
+ LCD_INSTR_CMD, 0x01,
+ LCD_INSTR_CMD, 0x08,
+ LCD_INSTR_DAT, 0x03,
+ LCD_INSTR_DAT, 0x00,
+
+ LCD_INSTR_CMD, 0x01,
+ LCD_INSTR_CMD, 0x11,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x01,
+
+ LCD_INSTR_CMD, 0x01,
+ LCD_INSTR_CMD, 0x35,
+ LCD_INSTR_DAT, 0x76,
+ LCD_INSTR_DAT, 0x66,
+
+ LCD_INSTR_CMD, 0x01,
+ LCD_INSTR_CMD, 0x39,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x26,
+
+ LCD_INSTR_CMD, 0x04,
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0xc7,
+
+ LCD_INSTR_CMD, 0x04,
+ LCD_INSTR_CMD, 0x01,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x00,
+
+ LCD_INSTR_CMD, 0x06,
+ LCD_INSTR_CMD, 0x06,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x00,
+
+ LCD_INSTR_CMD, 0x03,
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_DAT, 0x0d,
+ LCD_INSTR_DAT, 0x0e,
+
+ LCD_INSTR_CMD, 0x03,
+ LCD_INSTR_CMD, 0x01,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x03,
+
+ LCD_INSTR_CMD, 0x03,
+ LCD_INSTR_CMD, 0x02,
+ LCD_INSTR_DAT, 0x08,
+ LCD_INSTR_DAT, 0x08,
+
+ LCD_INSTR_CMD, 0x03,
+ LCD_INSTR_CMD, 0x03,
+ LCD_INSTR_DAT, 0x02,
+ LCD_INSTR_DAT, 0x01,
+
+ LCD_INSTR_CMD, 0x03,
+ LCD_INSTR_CMD, 0x04,
+ LCD_INSTR_DAT, 0x03,
+ LCD_INSTR_DAT, 0x01,
+
+ LCD_INSTR_CMD, 0x03,
+ LCD_INSTR_CMD, 0x05,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x04,
+
+ LCD_INSTR_CMD, 0x03,
+ LCD_INSTR_CMD, 0x06,
+ LCD_INSTR_DAT, 0x1b,
+ LCD_INSTR_DAT, 0x21,
+
+ LCD_INSTR_CMD, 0x03,
+ LCD_INSTR_CMD, 0x07,
+ LCD_INSTR_DAT, 0x0f,
+ LCD_INSTR_DAT, 0x0e,
+
+ LCD_INSTR_CMD, 0x03,
+ LCD_INSTR_CMD, 0x08,
+ LCD_INSTR_DAT, 0x01,
+ LCD_INSTR_DAT, 0x04,
+
+ LCD_INSTR_CMD, 0x03,
+ LCD_INSTR_CMD, 0x09,
+ LCD_INSTR_DAT, 0x08,
+ LCD_INSTR_DAT, 0x08,
+
+ LCD_INSTR_CMD, 0x03,
+ LCD_INSTR_CMD, 0x0a,
+ LCD_INSTR_DAT, 0x02,
+ LCD_INSTR_DAT, 0x01,
+
+ LCD_INSTR_CMD, 0x03,
+ LCD_INSTR_CMD, 0x0b,
+ LCD_INSTR_DAT, 0x03,
+ LCD_INSTR_DAT, 0x01,
+
+ LCD_INSTR_CMD, 0x03,
+ LCD_INSTR_CMD, 0x0c,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x03,
+
+ LCD_INSTR_CMD, 0x03,
+ LCD_INSTR_CMD, 0x0d,
+ LCD_INSTR_DAT, 0x31,
+ LCD_INSTR_DAT, 0x34,
+
+ /* X start */
+ LCD_INSTR_CMD, 0x02,
+ LCD_INSTR_CMD, 0x10,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x1e, /* 30 */
+
+ /* X end */
+ LCD_INSTR_CMD, 0x02,
+ LCD_INSTR_CMD, 0x11,
+ LCD_INSTR_DAT, 0x01,
+ LCD_INSTR_DAT, 0x85, /* 389 */
+
+ /* Y start */
+ LCD_INSTR_CMD, 0x02,
+ LCD_INSTR_CMD, 0x12,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x00, /* 0 */
+
+ /* Y end */
+ LCD_INSTR_CMD, 0x02,
+ LCD_INSTR_CMD, 0x13,
+ LCD_INSTR_DAT, 0x01,
+ LCD_INSTR_DAT, 0x8f, /* 399 */
+
+ /* RAM write start X? */
+ LCD_INSTR_CMD, 0x02,
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x1e,
+
+ /* RAM write start Y? */
+ LCD_INSTR_CMD, 0x02,
+ LCD_INSTR_CMD, 0x01,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x00,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x03,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x30,
+
+ LCD_INSTR_CMD, 0x02,
+ LCD_INSTR_CMD, 0x02,
+ LCD_INSTR_END,
+};
+
+/* NOTE this sleep mode may not be saving power, but it gets rid of the
+ * ghost image that would otherwise remain on the display */
+static const uint32_t q1_lcd_cmd_sleep[] = {
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x10,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x03,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x07,
+ LCD_INSTR_DAT, 0x01,
+ LCD_INSTR_DAT, 0x01,
+
+ LCD_INSTR_END,
+};
+
+static const uint32_t q1_lcd_cmd_wake[] = {
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x07,
+ LCD_INSTR_DAT, 0x01,
+ LCD_INSTR_DAT, 0x03,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x10,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0xc1,
+
+ LCD_INSTR_END,
+};
+
+static const uint8_t __attribute__((aligned(64)))
+ q1_lcd_dma_wr_cmd[] = {0x02, 0x02, 0x02, 0x02};
+
+const struct lcd_tgt_config lcd_tgt_config = {
+ .bus_width = 8,
+ .cmd_width = 8,
+ .use_6800_mode = 0,
+ .use_serial = 0,
+ .clk_polarity = 0,
+ .dc_polarity = 0,
+ .wr_polarity = 1,
+ .te_enable = 0,
+ .big_endian = 1,
+ .dma_wr_cmd_buf = &q1_lcd_dma_wr_cmd,
+ .dma_wr_cmd_size = sizeof(q1_lcd_dma_wr_cmd),
+};
+
+void lcd_tgt_enable(bool enable)
+{
+ if(enable) {
+ /* power on the panel */
+ gpio_set_level(GPIO_LCD_PWR, 1);
+ gpio_set_level(GPIO_LCD_RST, 1);
+ gpio_set_level(GPIO_LCD_CE, 1);
+ gpio_set_level(GPIO_LCD_RD, 1);
+ mdelay(50);
+ gpio_set_level(GPIO_LCD_RST, 0);
+ mdelay(100);
+ gpio_set_level(GPIO_LCD_RST, 1);
+ mdelay(50);
+ gpio_set_level(GPIO_LCD_CE, 0);
+
+ /* Start the controller */
+ lcd_set_clock(X1000_CLK_MPLL, 50000000);
+ lcd_exec_commands(q1_lcd_cmd_enable);
+ } else {
+ /* FIXME: Shanling Q1 LCD power down sequence
+ * not important because we don't use it but it'd be nice to know */
+ }
+}
+
+void lcd_tgt_sleep(bool sleep)
+{
+ if(sleep)
+ lcd_exec_commands(q1_lcd_cmd_sleep);
+ else
+ lcd_exec_commands(q1_lcd_cmd_wake);
+}
diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/power-shanlingq1.c b/firmware/target/mips/ingenic_x1000/shanlingq1/power-shanlingq1.c
new file mode 100644
index 0000000000..59a2262f25
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/shanlingq1/power-shanlingq1.c
@@ -0,0 +1,163 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 "power.h"
+#include "adc.h"
+#include "system.h"
+#include "axp-pmu.h"
+#ifdef HAVE_CW2015
+# include "cw2015.h"
+#endif
+#ifdef HAVE_USB_CHARGING_ENABLE
+# include "usb_core.h"
+#endif
+
+#include "i2c-x1000.h"
+
+/* TODO: Better(?) battery reporting for Q1 using CW2015 driver
+ *
+ * The CW2015 has its own quirks so the driver has to be more complicated
+ * than "read stuff from I2C," unfortunately. Without fixing the quirks it
+ * is probably worse than the simple voltage-based method.
+ *
+ * A bigger problem is that it shares an I2C bus with the AXP192, but when
+ * we attempt to communicate with both chips, they start returning bogus
+ * data intermittently. Ususally, reads will return 0 but sometimes they
+ * can return other nonzero bogus data. It could be that one or the other is
+ * pulling the bus line down inappropriately, or maybe the hardware does not
+ * respect the bus free time between start/stop conditions and one of the
+ * devices is getting confused.
+ */
+
+const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] =
+{
+ 3470
+};
+
+/* the OF shuts down at this voltage */
+const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] =
+{
+ 3400
+};
+
+/* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */
+const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11] =
+{
+ { 3400, 3639, 3697, 3723, 3757, 3786, 3836, 3906, 3980, 4050, 4159 }
+};
+
+/* voltages (millivolt) of 0%, 10%, ... 100% when charging enabled */
+const unsigned short percent_to_volt_charge[11] =
+{
+ 3485, 3780, 3836, 3857, 3890, 3930, 3986, 4062, 4158, 4185, 4196
+};
+
+void power_init(void)
+{
+ i2c_x1000_set_freq(AXP_PMU_BUS, I2C_FREQ_400K);
+ axp_init();
+#ifdef HAVE_CW2015
+ cw2015_init();
+#endif
+
+ /* Set lowest sample rate */
+ axp_adc_set_rate(AXP_ADC_RATE_25HZ);
+
+ /* Enable required ADCs */
+ axp_adc_set_enabled(
+ (1 << ADC_BATTERY_VOLTAGE) |
+ (1 << ADC_CHARGE_CURRENT) |
+ (1 << ADC_DISCHARGE_CURRENT) |
+ (1 << ADC_VBUS_VOLTAGE) |
+ (1 << ADC_VBUS_CURRENT) |
+ (1 << ADC_INTERNAL_TEMP) |
+ (1 << ADC_APS_VOLTAGE));
+
+ /* Change supply voltage from the default of 1250 mV to 1200 mV,
+ * this matches the original firmware's settings. Didn't observe
+ * any obviously bad behavior at 1250 mV, but better to be safe. */
+ axp_supply_set_voltage(AXP_SUPPLY_DCDC2, 1200);
+
+ /* For now, just turn everything on... definitely the touchscreen
+ * is powered by one of the outputs */
+ i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
+ AXP_REG_PWROUTPUTCTRL1, 0, 0x05, NULL);
+ i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
+ AXP_REG_PWROUTPUTCTRL2, 0, 0x0f, NULL);
+ i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
+ AXP_REG_DCDCWORKINGMODE, 0, 0xc0, NULL);
+
+ /* Delay to give power output time to stabilize */
+ mdelay(20);
+}
+
+#ifdef HAVE_USB_CHARGING_ENABLE
+void usb_charging_maxcurrent_change(int maxcurrent)
+{
+ axp_set_charge_current(maxcurrent);
+}
+#endif
+
+void power_off(void)
+{
+ axp_power_off();
+ while(1);
+}
+
+bool charging_state(void)
+{
+ return axp_battery_status() == AXP_BATT_CHARGING;
+}
+
+int _battery_voltage(void)
+{
+ /* CW2015 can also read battery voltage, but the AXP consistently
+ * reads ~20-30 mV higher so I suspect it's the "real" voltage. */
+ return axp_adc_read(ADC_BATTERY_VOLTAGE);
+}
+
+#if CONFIG_BATTERY_MEASURE & CURRENT_MEASURE
+int _battery_current(void)
+{
+ if(charging_state())
+ return axp_adc_read(ADC_CHARGE_CURRENT);
+ else
+ return axp_adc_read(ADC_DISCHARGE_CURRENT);
+}
+#endif
+
+#if defined(HAVE_CW2015) && (CONFIG_BATTERY_MEASURE & PERCENTAGE_MEASURE) != 0
+int _battery_level(void)
+{
+ return cw2015_get_soc();
+}
+#endif
+
+#if defined(HAVE_CW2015) && (CONFIG_BATTERY_MEASURE & TIME_MEASURE) != 0
+int _battery_time(void)
+{
+ return cw2015_get_rrt();
+}
+#endif
+
+void adc_init(void)
+{
+}
diff --git a/firmware/target/arm/pnx0101/iriver-ifp7xx/adc-ifp7xx.c b/firmware/target/mips/ingenic_x1000/spl-nand-x1000.c
index 0f306f9986..24eb42081e 100644
--- a/firmware/target/arm/pnx0101/iriver-ifp7xx/adc-ifp7xx.c
+++ b/firmware/target/mips/ingenic_x1000/spl-nand-x1000.c
@@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Copyright (C) 2006 by Barry Wardell
+ * Copyright (C) 2021 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -18,42 +18,33 @@
* KIND, either express or implied.
*
****************************************************************************/
-#include "config.h"
-#include "cpu.h"
-#include "system.h"
-#include "kernel.h"
-#include "thread.h"
-#include "adc.h"
-static unsigned short adcdata[NUM_ADC_CHANNELS];
+#include "spl-x1000.h"
+#include "gpio-x1000.h"
+#include "nand-x1000.h"
-unsigned short adc_read(int channel)
+static struct nand_drv* ndrv = NULL;
+
+int spl_storage_open(void)
{
- return adcdata[channel];
+ /* We need to assign the GPIOs manually */
+ gpioz_configure(GPIO_A, 0x3f << 26, GPIOF_DEVICE(1));
+
+ /* Allocate NAND driver manually in DRAM */
+ ndrv = spl_alloc(sizeof(struct nand_drv));
+ ndrv->page_buf = spl_alloc(NAND_DRV_MAXPAGESIZE);
+ ndrv->scratch_buf = spl_alloc(NAND_DRV_SCRATCHSIZE);
+ ndrv->refcount = 0;
+
+ return nand_open(ndrv);
}
-static void adc_tick(void)
+void spl_storage_close(void)
{
- if (ADCST & 0x10) {
- adcdata[0] = ADCCH0 & 0x3ff;
- adcdata[1] = ADCCH1 & 0x3ff;
- adcdata[2] = ADCCH2 & 0x3ff;
- adcdata[3] = ADCCH3 & 0x3ff;
- adcdata[4] = ADCCH4 & 0x3ff;
- ADCST = 0xa;
- }
+ nand_close(ndrv);
}
-void adc_init(void)
+int spl_storage_read(uint32_t addr, uint32_t length, void* buffer)
{
- ADCR24 = 0xaaaaa;
- ADCR28 = 0;
- ADCST = 2;
- ADCST = 0xa;
-
- while (!(ADCST & 0x10));
- adc_tick();
-
- tick_add_task(adc_tick);
+ return nand_read_bytes(ndrv, addr, length, buffer);
}
-
diff --git a/firmware/target/mips/ingenic_x1000/spl-start.S b/firmware/target/mips/ingenic_x1000/spl-start.S
new file mode 100644
index 0000000000..ecdc47f283
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/spl-start.S
@@ -0,0 +1,97 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 "mips.h"
+
+ .text
+ .extern spl_main
+ .global _spl_start
+
+ .set push
+ .set mips32
+ .set noreorder
+ .set noat
+
+ .section .startup.spl
+
+_spl_start:
+ /* Clear data watchpoint */
+ mtc0 zero, C0_WATCHLO
+ mtc0 zero, C0_WATCHHI
+
+ /* Set BEV, ERL, mask interrupts */
+ li v0, 0x40fc04
+ mtc0 v0, C0_Status
+
+ /* Set Cause_IV to 1 (use special interrupt vector) */
+ li v0, M_CauseIV
+ mtc0 v0, C0_Cause
+
+ /* Set CPU_MODE and BUS_MODE to 1 in CPM_OPCR (Ingenic does this) */
+ lui v0, 0xb000
+ lw v1, 0x24(v0)
+ ori v1, v1, 0x22
+ sw v1, 0x24(v0)
+
+ /* Enable kseg0 cacheability */
+ li v0, 3
+ mtc0 v0, C0_Config
+ nop
+
+ /* According to ingenic: "enable idx-store-data cache insn" */
+ li v0, 0x20000000
+ mtc0 v0, C0_ErrCtl
+
+ /* Cache init */
+ li v0, 0x80000000
+ ori v1, v0, 0x4000
+ mtc0 zero, C0_TAGLO
+ mtc0 zero, C0_TAGHI
+_cache_loop:
+ cache ICIndexStTag, 0(v0)
+ cache DCIndexStTag, 0(v0)
+ addiu v0, v0, 32
+ bne v0, v1, _cache_loop
+ nop
+
+ /* Invalidate BTB */
+ mfc0 v0, C0_Config, 7
+ nop
+ ori v0, v0, 2
+ mtc0 v0, C0_Config, 7
+ nop
+
+ /* Clear the BSS segment (needed to zero-initialize C static values) */
+ la t0, _bssbegin
+ la t1, _bssend
+ beq t0, t1, _bss_done
+_bss_loop:
+ addiu t0, 4
+ bne t0, t1, _bss_loop
+ sw zero, -4(t0)
+_bss_done:
+
+ /* Jump to C code */
+ j spl_main
+ nop
+
+ .set pop
diff --git a/firmware/target/mips/ingenic_x1000/spl-x1000.c b/firmware/target/mips/ingenic_x1000/spl-x1000.c
new file mode 100644
index 0000000000..08f88f506c
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/spl-x1000.c
@@ -0,0 +1,371 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 "spl-x1000.h"
+#include "clk-x1000.h"
+#include "nand-x1000.h"
+#include "gpio-x1000.h"
+#include "boot-x1000.h"
+#include "x1000/cpm.h"
+#include "x1000/ost.h"
+#include "x1000/uart.h"
+#include "x1000/ddrc.h"
+#include "x1000/ddrc_apb.h"
+#include "x1000/ddrphy.h"
+#include "ucl_decompress.h"
+#include <string.h>
+
+#if defined(FIIO_M3K)
+/* Size of memory, either 64 or 32 is legal. */
+# define SPL_DDR_MEMORYSIZE 64
+/* Pin to flash on spl_error(). Should be a backlight. */
+# define SPL_ERROR_PIN GPIO_PC(24)
+/* Address and size of the bootloader on the storage medium used by the SPL */
+# define BOOT_STORAGE_ADDR 0x6800
+# define BOOT_STORAGE_SIZE (102 * 1024)
+#elif defined(SHANLING_Q1)
+# define SPL_DDR_MEMORYSIZE 64
+# define SPL_ERROR_PIN GPIO_PC(25)
+# define BOOT_STORAGE_ADDR 0x6800
+# define BOOT_STORAGE_SIZE (102 * 1024)
+#elif defined(EROS_QN)
+# define SPL_DDR_MEMORYSIZE 32
+# define SPL_ERROR_PIN GPIO_PC(25)
+# define BOOT_STORAGE_ADDR 0x6800
+# define BOOT_STORAGE_SIZE (102 * 1024)
+#else
+# error "please define SPL config"
+#endif
+
+/* Hardcode this since the SPL is considered part of the bootloader,
+ * and should never get built or updated separately. */
+#define BOOT_LOAD_ADDR X1000_DRAM_BASE
+#define BOOT_EXEC_ADDR BOOT_LOAD_ADDR
+
+/* Whether the bootloader is UCL-compressed */
+#ifndef SPL_USE_UCLPACK
+# define SPL_USE_UCLPACK 1
+#endif
+
+/* Whether auto-self-refresh should be enabled (seems it always should be?) */
+#ifndef SPL_DDR_AUTOSR_EN
+# define SPL_DDR_AUTOSR_EN 1
+#endif
+
+/* Whether DLL bypass is necessary (probably always?) */
+#ifndef SPL_DDR_NEED_BYPASS
+# define SPL_DDR_NEED_BYPASS 1
+#endif
+
+static void* heap = (void*)(X1000_SDRAM_BASE + X1000_SDRAM_SIZE);
+
+void* spl_alloc(size_t count)
+{
+ heap -= CACHEALIGN_UP(count);
+ memset(heap, 0, CACHEALIGN_UP(count));
+ return heap;
+}
+
+void spl_error(void)
+{
+ int level = 0;
+ while(1) {
+ gpio_set_function(SPL_ERROR_PIN, GPIOF_OUTPUT(level));
+ mdelay(100);
+ level = 1 - level;
+ }
+}
+
+static void init_ost(void)
+{
+ /* NOTE: the prescaler needs to be the same as in system-x1000.c */
+ jz_writef(CPM_CLKGR, OST(0));
+ jz_writef(OST_CTRL, PRESCALE2_V(BY_4));
+ jz_overwritef(OST_CLEAR, OST2(1));
+ jz_write(OST_2CNTH, 0);
+ jz_write(OST_2CNTL, 0);
+ jz_setf(OST_ENABLE, OST2);
+}
+
+/* NOTE: This is originally based on disassembly of the FiiO M3K SPL, which
+ * is in fact the GPL'd Ingenic X-Loader SPL. Similar stuff can be found in
+ * Ingenic's U-boot code.
+ *
+ * The source code for the Ingenic X-Loader SPL can be found in these repos:
+ * - https://github.com/JaminCheung/x-loader
+ * - https://github.com/YuanhuanLiang/X1000
+ *
+ * Runtime conditionals based on the SoC type, looked up by OTP bits in EFUSE,
+ * are converted to compile-time conditionals here, as they are constant for a
+ * given target and there is no point in wasting precious space on dead code.
+ *
+ * I didn't decode the register fields; note that some values are documented as
+ * "reserved" in the X1000 PM. The X-Loader source might shed more light on it;
+ * it's likely these bits have meaning only on other Ingenic SoCs.
+ *
+ * The DDR PHY registers match Synopsys's "PHY Utility Block Lite." The names
+ * of those registers & their fields can also be found in the X-Loader code,
+ * but they're not documented by Ingenic.
+ */
+static int init_dram(void)
+{
+#if SPL_DDR_MEMORYSIZE != 64 && SPL_DDR_MEMORYSIZE != 32
+# error "bad memory size"
+#endif
+
+ jz_writef(CPM_CLKGR, DDR(0));
+
+ REG_CPM_DRCG = 0x73;
+ mdelay(3);
+ REG_CPM_DRCG = 0x71;
+ mdelay(3);
+ REG_DDRC_APB_PHYRST_CFG = 0x1a00001;
+ mdelay(3);
+ REG_DDRC_APB_PHYRST_CFG = 0;
+ mdelay(3);
+ REG_DDRC_CTRL = 0xf00000;
+ mdelay(3);
+ REG_DDRC_CTRL = 0;
+ mdelay(3);
+
+#if SPL_DDR_MEMORYSIZE == 64
+ REG_DDRC_CFG = 0xa468a6c;
+#elif SPL_DDR_MEMORYSIZE == 32
+ REG_DDRC_CFG = 0xa46896c;
+#endif
+ REG_DDRC_CTRL = 2;
+ REG_DDRPHY_DTAR = 0x150000;
+ REG_DDRPHY_DCR = 0;
+ REG_DDRPHY_MR0 = 0x42;
+ REG_DDRPHY_MR2 = 0x98;
+ REG_DDRPHY_PTR0 = 0x21000a;
+ REG_DDRPHY_PTR1 = 0xa09c40;
+ REG_DDRPHY_PTR2 = 0x280014;
+ REG_DDRPHY_DTPR0 = 0x1a69444a;
+ REG_DDRPHY_DTPR1 = 0x180090;
+ REG_DDRPHY_DTPR2 = 0x1ff99428;
+ REG_DDRPHY_DXGCR(0) = 0x90881;
+ REG_DDRPHY_DXGCR(1) = 0x90881;
+ REG_DDRPHY_DXGCR(2) = 0x90e80;
+ REG_DDRPHY_DXGCR(3) = 0x90e80;
+ REG_DDRPHY_PGCR = 0x1042e03;
+ REG_DDRPHY_ACIOCR = 0x30c00813;
+ REG_DDRPHY_DXCCR = 0x4912;
+
+ int i = 10000;
+ while(i > 0 && REG_DDRPHY_PGSR != 7 && REG_DDRPHY_PGSR != 0x1f)
+ i -= 1;
+ if(i == 0)
+ return 1;
+
+#if SPL_DDR_NEED_BYPASS
+ REG_DDRPHY_ACDLLCR = 0x80000000;
+ REG_DDRPHY_DSGCR &= ~0x10;
+ REG_DDRPHY_DLLGCR |= 0x800000;
+ REG_DDRPHY_PIR = 0x20020041;
+#else
+ REG_DDRPHY_PIR = 0x41;
+#endif
+
+ while(i > 0 && REG_DDRPHY_PGSR != 0xf && REG_DDRPHY_PGSR != 0x1f)
+ i -= 1;
+ if(i == 0)
+ return 2;
+
+ REG_DDRC_APB_PHYRST_CFG = 0x400000;
+ mdelay(3);
+ REG_DDRC_APB_PHYRST_CFG = 0;
+ mdelay(3);
+
+#if SPL_DDR_MEMORYSIZE == 64
+ REG_DDRC_CFG = 0xa468aec;
+#elif SPL_DDR_MEMORYSIZE == 32
+ REG_DDRC_CFG = 0xa4689ec;
+#endif
+ REG_DDRC_CTRL = 2;
+#if SPL_DDR_NEED_BYPASS
+ REG_DDRPHY_PIR = 0x20020081;
+#else
+ REG_DDRPHY_PIR = 0x85;
+#endif
+
+ i = 500000;
+ while(REG_DDRPHY_PGSR != 0x1f) {
+ if(REG_DDRPHY_PGSR & 0x70)
+ break;
+ i -= 1;
+ }
+
+ if(i == 0)
+ return 3;
+
+ if((REG_DDRPHY_PGSR & 0x60) != 0 && REG_DDRPHY_PGSR != 0)
+ return 4;
+
+ REG_DDRC_CTRL = 0;
+ REG_DDRC_CTRL = 10;
+ REG_DDRC_CTRL = 0;
+#if SPL_DDR_MEMORYSIZE == 64
+ REG_DDRC_CFG = 0xa468a6c;
+#elif SPL_DDR_MEMORYSIZE == 32
+ REG_DDRC_CFG = 0xa46896c;
+#endif
+ REG_DDRC_TIMING1 = 0x2050501;
+ REG_DDRC_TIMING2 = 0x4090404;
+ REG_DDRC_TIMING3 = 0x2704030d;
+ REG_DDRC_TIMING4 = 0xb7a0251;
+ REG_DDRC_TIMING5 = 0xff090200;
+ REG_DDRC_TIMING6 = 0xa0a0202;
+#if SPL_DDR_MEMORYSIZE == 64
+ REG_DDRC_MMAP0 = 0x20fc;
+ REG_DDRC_MMAP1 = 0x2400;
+#elif SPL_DDR_MEMORYSIZE == 32
+ REG_DDRC_MMAP0 = 0x20fe;
+ REG_DDRC_MMAP1 = 0x2200;
+#endif
+ REG_DDRC_CTRL = 10;
+ REG_DDRC_REFCNT = 0x2f0003; /* is this adjustable for 32M? */
+ REG_DDRC_CTRL = 0xc91e;
+
+#if SPL_DDR_MEMORYSIZE == 64
+ REG_DDRC_REMAP1 = 0x03020c0b;
+ REG_DDRC_REMAP2 = 0x07060504;
+ REG_DDRC_REMAP3 = 0x000a0908;
+ REG_DDRC_REMAP4 = 0x0f0e0d01;
+ REG_DDRC_REMAP5 = 0x13121110;
+#elif SPL_DDR_MEMORYSIZE == 32
+ REG_DDRC_REMAP1 = 0x03020b0a;
+ REG_DDRC_REMAP2 = 0x07060504;
+ REG_DDRC_REMAP3 = 0x01000908;
+ REG_DDRC_REMAP4 = 0x0f0e0d0c;
+ REG_DDRC_REMAP5 = 0x13121110;
+#endif
+
+ REG_DDRC_STATUS &= ~0x40;
+
+#if SPL_DDR_AUTOSR_EN
+#if SPL_DDR_NEED_BYPASS
+ jz_writef(CPM_DDRCDR, GATE_EN(1));
+ REG_DDRC_APB_CLKSTP_CFG = 0x9000000f;
+#else
+ REG_DDRC_DLP = 0;
+#endif
+#endif
+
+ REG_DDRC_AUTOSR_EN = SPL_DDR_AUTOSR_EN;
+ return 0;
+}
+
+static void* get_load_buffer(void)
+{
+ /* read to a temporary location if we need to decompress,
+ * otherwise simply read directly to the load address. */
+ if(SPL_USE_UCLPACK)
+ return spl_alloc(BOOT_STORAGE_SIZE);
+ else
+ return (void*)BOOT_LOAD_ADDR;
+}
+
+/* Mapping of boot_sel[1:0] pins.
+ * See X1000 PM pg. 687, "XBurst Boot ROM Specification" */
+enum {
+ BSEL_MSC = 1,
+ BSEL_USB = 2,
+ BSEL_SFC = 3,
+};
+
+static uint32_t get_boot_sel(void)
+{
+ /* This variable holds the level of the boot_sel[2:0] pins at boot time,
+ * and is defined by the maskrom.
+ *
+ * We use it to detect when we're USB booting, but this isn't totally
+ * accurate because it only reflects the selected boot mode at reset and
+ * not the current mode -- if the original selection fails and we fall
+ * back to USB, this variable will only return the original selection.
+ */
+ return (*(uint32_t*)0xf40001ec) & 3;
+}
+
+void spl_main(void)
+{
+ int rc;
+ void* load_buffer;
+
+ /* magic */
+ REG_CPM_PSWC0ST = 0x00;
+ REG_CPM_PSWC1ST = 0x10;
+ REG_CPM_PSWC2ST = 0x18;
+ REG_CPM_PSWC3ST = 0x08;
+
+ /* Save this, it's needed on some targets */
+ uint32_t saved_cpm_scratch = REG_CPM_SCRATCH;
+
+ /* set up boot flags */
+ init_boot_flags();
+
+ /* early clock and DRAM init */
+ clk_init_early();
+ init_ost();
+ if(init_dram() != 0)
+ spl_error();
+
+ /* USB boot stops here */
+ if(get_boot_sel() == BSEL_USB) {
+ set_boot_flag(BOOT_FLAG_USB_BOOT);
+ return;
+ }
+
+ /* finish up clock init */
+ clk_init();
+
+ /* load the image from storage */
+ rc = spl_storage_open();
+ if(rc != 0)
+ spl_error();
+
+ load_buffer = get_load_buffer();
+ rc = spl_storage_read(BOOT_STORAGE_ADDR, BOOT_STORAGE_SIZE, load_buffer);
+ if(rc != 0)
+ spl_error();
+
+ /* decompress */
+ if(SPL_USE_UCLPACK) {
+ uint32_t out_size = X1000_SDRAM_END - BOOT_LOAD_ADDR;
+ rc = ucl_unpack((uint8_t*)load_buffer, BOOT_STORAGE_SIZE,
+ (uint8_t*)BOOT_LOAD_ADDR, &out_size);
+ } else {
+ rc = 0;
+ }
+
+ if(rc != 0)
+ spl_error();
+
+ /* close off storage access */
+ spl_storage_close();
+
+ /* jump to the entry point */
+ typedef void(*entry_fn)(uint32_t);
+ entry_fn fn = (entry_fn)BOOT_EXEC_ADDR;
+ commit_discard_idcache();
+ fn(saved_cpm_scratch);
+}
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/nand-target.h b/firmware/target/mips/ingenic_x1000/spl-x1000.h
index 26a8b840c9..9ee1aa768e 100644
--- a/firmware/target/mips/ingenic_x1000/fiiom3k/nand-target.h
+++ b/firmware/target/mips/ingenic_x1000/spl-x1000.h
@@ -19,24 +19,30 @@
*
****************************************************************************/
-#ifndef __NAND_TARGET_H__
-#define __NAND_TARGET_H__
+#ifndef __SPL_X1000_H__
+#define __SPL_X1000_H__
-/* The max page size (main + spare) of all NAND chips used by this target */
-#define NAND_MAX_PAGE_SIZE (2048 + 64)
+#include "boot-x1000.h"
+#include <stddef.h>
+#include <stdint.h>
-/* The clock source to use for the SFC controller. Note the SPL has special
- * handling which ignores this choice, so it only applies to bootloader & app.
- */
-#define NAND_CLOCK_SOURCE X1000_CLK_SCLK_A
+/* Memory allocator. Allocation starts from the top of DRAM and counts down.
+ * Allocation sizes are rounded up to a multiple of the cacheline size, so
+ * the returned address is always suitably aligned for DMA. */
+extern void* spl_alloc(size_t count);
-/* The clock speed to use for the SFC controller during chip identification */
-#define NAND_INIT_CLOCK_SPEED 150000000
+/* Access to boot storage medium, eg. flash or MMC/SD card.
+ *
+ * Read address and length is given in bytes. To make life easier, no
+ * alignment restrictions are placed on the buffer, length, or address.
+ * The buffer doesn't even need to be in DRAM.
+ */
+extern int spl_storage_open(void);
+extern void spl_storage_close(void);
+extern int spl_storage_read(uint32_t addr, uint32_t length, void* buffer);
-/* Initial value to program SFC_DEV_CONF register with */
-#define NAND_INIT_SFC_DEV_CONF \
- jz_orf(SFC_DEV_CONF, CE_DL(1), HOLD_DL(1), WP_DL(1), \
- CPHA(0), CPOL(0), TSH(7), TSETUP(0), THOLD(0), \
- STA_TYPE_V(1BYTE), CMD_TYPE_V(8BITS), SMP_DELAY(1))
+/* Called on a fatal error -- it should do something visible to the user
+ * like flash the backlight repeatedly. */
+extern void spl_error(void) __attribute__((noreturn));
-#endif /* __NAND_TARGET_H__ */
+#endif /* __SPL_X1000_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/spl.lds b/firmware/target/mips/ingenic_x1000/spl.lds
index ab4a2720f3..b3e508e9c3 100644
--- a/firmware/target/mips/ingenic_x1000/spl.lds
+++ b/firmware/target/mips/ingenic_x1000/spl.lds
@@ -1,31 +1,24 @@
#include "config.h"
+#include "cpu.h"
OUTPUT_FORMAT("elf32-littlemips")
OUTPUT_ARCH(MIPS)
-ENTRY(_start)
-STARTUP(target/mips/ingenic_x1000/crt0.o)
+ENTRY(_spl_start)
+STARTUP(target/mips/ingenic_x1000/spl-start.o)
-#define DRAMORIG 0x80000000
-#define DRAMSIZE (MEMORYSIZE * 0x100000)
-#define USED_DRAM 16K
-
-/* TCSM is 16 KiB and is mapped starting at address 0xf4000000.
- *
- * The SPL is loaded to TCSM + 0x1000. The area below that is stack space.
- * The first 2 KiB of SPL is just headers. The code begins at TCSM + 0x1800.
- * The maskrom will jump to that address (via jalr) after loading the SPL.
- */
MEMORY {
- TCSM : ORIGIN = 0xf4001800, LENGTH = 0x2800
- DRAM : ORIGIN = DRAMORIG + DRAMSIZE - USED_DRAM, LENGTH = USED_DRAM
+ TCSM : ORIGIN = X1000_SPL_EXEC_ADDR,
+ LENGTH = X1000_SPL_SIZE
}
SECTIONS
{
.text :
{
- *(.init.text);
+ *(.startup.spl);
*(.text*);
+ *(.icode*);
+ *(.init*);
} > TCSM
. = ALIGN(4);
@@ -52,8 +45,10 @@ SECTIONS
_bssend = .;
} > TCSM
- .sdram (NOLOAD) :
+ /DISCARD/ :
{
- *(.sdram);
- } > DRAM
+ *(.MIPS.abiflags);
+ *(.eh_frame);
+ *(.rel.dyn);
+ }
}
diff --git a/firmware/target/mips/ingenic_x1000/system-target.h b/firmware/target/mips/ingenic_x1000/system-target.h
index 050afcca9e..ed077a3cce 100644
--- a/firmware/target/mips/ingenic_x1000/system-target.h
+++ b/firmware/target/mips/ingenic_x1000/system-target.h
@@ -22,36 +22,24 @@
#ifndef __SYSTEM_TARGET_H__
#define __SYSTEM_TARGET_H__
-/* For the sake of system.h CACHEALIGN macros.
- * We need this to align DMA buffers, etc.
- */
-#define CACHEALIGN_BITS 5
-#define CACHE_SIZE (16*1024)
-
-#ifdef BOOTLOADER_SPL
-/* This saves ~200 bytes in the SPL by allowing -ffunction-sections to split
- * up the cache management functions, most of which aren't called by the SPL.
- * If they are placed in .icode, then they all end up in one section and the
- * linker can't discard the unused functions.
- */
-# define MIPS_CACHEFUNC_ATTR
+#ifdef DEBUG
+/* Define this to get CPU idle stats, visible in the debug menu. */
+# define X1000_CPUIDLE_STATS
#endif
+#include "cpu.h"
#include "mmu-mips.h"
#include "mipsregs.h"
#include "mipsr2-endian.h"
+#include "system-mips.h"
#include <stdint.h>
-/* Get physical address for DMA */
-#define PHYSADDR(addr) (((unsigned long)(addr)) & 0x1fffffff)
-
-#define HIGHEST_IRQ_LEVEL 0
-
/* Rockbox API */
#define enable_irq() set_c0_status(ST0_IE)
#define disable_irq() clear_c0_status(ST0_IE)
#define disable_irq_save() set_irq_level(0)
#define restore_irq(arg) write_c0_status(arg)
+#define HIGHEST_IRQ_LEVEL 0
static inline int set_irq_level(int lev)
{
@@ -66,16 +54,20 @@ static inline int set_irq_level(int lev)
return oldreg;
}
+#ifdef X1000_CPUIDLE_STATS
/* CPU idle stats, updated each kernel tick in kernel-x1000.c */
extern int __cpu_idle_avg;
extern int __cpu_idle_cur;
extern uint32_t __cpu_idle_ticks;
extern uint32_t __cpu_idle_reftick;
+#endif
static inline uint32_t __ost_read32(void);
static inline void core_sleep(void)
{
+#ifdef X1000_CPUIDLE_STATS
uint32_t t1 = __ost_read32();
+#endif
__asm__ __volatile__(
".set push\n\t"
@@ -90,16 +82,23 @@ static inline void core_sleep(void)
".set pop\n\t"
::: "t0", "t1", "t2");
+#ifdef X1000_CPUIDLE_STATS
uint32_t t2 = __ost_read32();
__cpu_idle_ticks += t2 - t1;
+#endif
enable_irq();
}
/* IRQ control */
+typedef void(*irq_handler_t)(void);
+
+extern irq_handler_t system_set_irq_handler(int irq, irq_handler_t handler);
extern void system_enable_irq(int irq);
extern void system_disable_irq(int irq);
+extern void system_early_init(void) INIT_ATTR;
+
/* Simple delay API */
#define OST_FREQUENCY (X1000_EXCLK_FREQ / 4)
#define OST_TICKS_PER_US (OST_FREQUENCY / 1000000)
diff --git a/firmware/target/mips/ingenic_x1000/system-x1000.c b/firmware/target/mips/ingenic_x1000/system-x1000.c
index 54513cffb2..64890a6c3a 100644
--- a/firmware/target/mips/ingenic_x1000/system-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/system-x1000.c
@@ -27,6 +27,7 @@
#include "dma-x1000.h"
#include "irq-x1000.h"
#include "clk-x1000.h"
+#include "boot-x1000.h"
#include "x1000/cpm.h"
#include "x1000/ost.h"
#include "x1000/tcu.h"
@@ -35,46 +36,15 @@
#include "x1000/msc.h"
#include "x1000/aic.h"
+#ifdef X1000_CPUIDLE_STATS
int __cpu_idle_avg = 0;
int __cpu_idle_cur = 0;
uint32_t __cpu_idle_ticks = 0;
uint32_t __cpu_idle_reftick = 0;
-
-static void system_init_clk(void)
-{
- /* Gate all clocks except CPU/bus/memory/RTC */
- REG_CPM_CLKGR = ~jz_orm(CPM_CLKGR, CPU_BIT, DDR, AHB0, APB0, RTC);
-
- /* Switch to EXCLK */
- clk_set_ccr_mux(CLKMUX_SCLK_A(EXCLK) | CLKMUX_CPU(SCLK_A) |
- CLKMUX_AHB0(SCLK_A) | CLKMUX_AHB2(SCLK_A));
- clk_set_ccr_div(1, 1, 1, 1, 1);
-
-#ifdef FIIO_M3K
- /* Nominal clock configuration
- * ---------------------------
- * APLL at 1 GHz, MPLL disabled
- * CPU at 1 GHz, L2 cache at 500 MHz
- * AHB0 and AHB2 at 200 MHz
- * PCLK at 100 MHz
- * DDR at 200 MHz
- */
- jz_writef(CPM_APCR, BS(1), PLLM(41), PLLN(0), PLLOD(0), ENABLE(1));
- while(jz_readf(CPM_APCR, ON) == 0);
-
- clk_set_ccr_div(1, 2, 5, 5, 10);
- clk_set_ccr_mux(CLKMUX_SCLK_A(APLL) | CLKMUX_CPU(SCLK_A) |
- CLKMUX_AHB0(SCLK_A) | CLKMUX_AHB2(SCLK_A));
- clk_set_ddr(X1000_CLK_SCLK_A, 5);
-
- /* Shut off MPLL, since nobody should be using it now */
- jz_writef(CPM_MPCR, ENABLE(0));
-#else
-# error "Please define system clock configuration for target"
#endif
-}
/* Prepare the CPU to process interrupts, but don't enable them yet */
+static void system_init_irq(void) INIT_ATTR;
static void system_init_irq(void)
{
/* Mask all interrupts */
@@ -91,11 +61,31 @@ static void system_init_irq(void)
write_c0_cause(M_CauseIV);
}
+/* First function called by crt0.S */
+void system_early_init(void)
+{
+#if defined(FIIO_M3K) && !defined(BOOTLOADER)
+ /* HACK for compatibility: CPM scratchpad has undefined contents at
+ * time of reset and old bootloader revisions don't initialize it.
+ * Therefore we can't rely on its contents on the FiiO M3K. This does
+ * kind of break the entire point of boot flags, but right now they
+ * are really only used by the bootloader so it's not a huge issue.
+ * This hack should keep everything working as usual. */
+ if(jz_readf(CPM_MPCR, ON) == 0) {
+ init_boot_flags();
+ set_boot_flag(BOOT_FLAG_CLK_INIT);
+ }
+#endif
+
+ /* Finish up clock init */
+ clk_init();
+}
+
/* First thing called from Rockbox main() */
void system_init(void)
{
- /* Setup system clocks */
- system_init_clk();
+ /* Gate all clocks except CPU/bus/memory/RTC */
+ REG_CPM_CLKGR = ~jz_orm(CPM_CLKGR, CPU_BIT, DDR, AHB0, APB0, RTC);
/* Ungate timers and turn them all off by default */
jz_writef(CPM_CLKGR, TCU(0), OST(0));
@@ -223,6 +213,8 @@ static void UIRQ(void)
}
#define intr(name) extern __attribute__((weak, alias("UIRQ"))) void name(void)
+/* DWC2 USB interrupt */
+#define OTG INT_USB_FUNC
/* Main interrupts */
intr(DMIC); intr(AIC); intr(SFC); intr(SSI0); intr(OTG); intr(AES);
@@ -263,7 +255,7 @@ intr(OST);
#undef intr
-static void(*const irqvector[])(void) = {
+static void(*irqvector[])(void) = {
/* ICSR0: 0 - 31 */
DMIC, AIC, UIRQ, UIRQ, UIRQ, UIRQ, UIRQ, SFC,
SSI0, UIRQ, PDMA, PDMAD, UIRQ, UIRQ, UIRQ, UIRQ,
@@ -293,6 +285,13 @@ static void(*const irqvector[])(void) = {
GPIOD00, GPIOD01, GPIOD02, GPIOD03, GPIOD04, GPIOD05,
};
+irq_handler_t system_set_irq_handler(int irq, irq_handler_t handler)
+{
+ irq_handler_t old_handler = irqvector[irq];
+ irqvector[irq] = handler;
+ return old_handler;
+}
+
void system_enable_irq(int irq)
{
if(IRQ_IS_GROUP0(irq)) {
@@ -345,8 +344,10 @@ static int vector_irq(void)
return n;
}
-void intr_handler(unsigned cause)
+void intr_handler(void)
{
+ unsigned long cause = read_c0_cause();
+
/* OST interrupt is handled separately */
if(cause & M_CauseIP3) {
OST();
@@ -365,49 +366,6 @@ void intr_handler(unsigned cause)
irqvector[irq]();
}
-void tlb_refill_handler(void)
-{
- panicf("TLB refill handler at 0x%08lx! [0x%x]",
- read_c0_epc(), read_c0_badvaddr());
-}
-
-#define EXC(x,y) case (x): return (y);
-static char* parse_exception(unsigned cause)
-{
- switch(cause & M_CauseExcCode)
- {
- EXC(EXC_INT, "Interrupt");
- EXC(EXC_MOD, "TLB Modified");
- EXC(EXC_TLBL, "TLB Exception (Load or Ifetch)");
- EXC(EXC_ADEL, "Address Error (Load or Ifetch)");
- EXC(EXC_ADES, "Address Error (Store)");
- EXC(EXC_TLBS, "TLB Exception (Store)");
- EXC(EXC_IBE, "Instruction Bus Error");
- EXC(EXC_DBE, "Data Bus Error");
- EXC(EXC_SYS, "Syscall");
- EXC(EXC_BP, "Breakpoint");
- EXC(EXC_RI, "Reserved Instruction");
- EXC(EXC_CPU, "Coprocessor Unusable");
- EXC(EXC_OV, "Overflow");
- EXC(EXC_TR, "Trap Instruction");
- EXC(EXC_FPE, "Floating Point Exception");
- EXC(EXC_C2E, "COP2 Exception");
- EXC(EXC_MDMX, "MDMX Exception");
- EXC(EXC_WATCH, "Watch Exception");
- EXC(EXC_MCHECK, "Machine Check Exception");
- EXC(EXC_CacheErr, "Cache error caused re-entry to Debug Mode");
- default:
- return 0;
- }
-}
-#undef EXC
-
-void exception_handler(unsigned cause, unsigned epc, unsigned stack_ptr)
-{
- panicf("Exception occurred: %s [0x%08x] at 0x%08x (stack at 0x%08x)",
- parse_exception(cause), read_c0_badvaddr(), epc, stack_ptr);
-}
-
void system_exception_wait(void)
{
#ifdef FIIO_M3K
diff --git a/firmware/target/mips/ingenic_x1000/usb-x1000.c b/firmware/target/mips/ingenic_x1000/usb-x1000.c
new file mode 100644
index 0000000000..398528c6c4
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/usb-x1000.c
@@ -0,0 +1,214 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * 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 "usb.h"
+#include "usb_core.h"
+#include "usb_drv.h"
+#include "usb-designware.h"
+#include "irq-x1000.h"
+#include "gpio-x1000.h"
+#include "x1000/cpm.h"
+
+/*
+ * USB-Designware driver API
+ */
+
+const struct usb_dw_config usb_dw_config = {
+ .phytype = DWC_PHYTYPE_UTMI_16,
+
+ /* Available FIFO memory: 3576 words
+ * Number of endpoints: 9
+ * Max packet size: 512 bytes
+ */
+ .rx_fifosz = 816, /* shared RxFIFO */
+ .nptx_fifosz = 32, /* only used for EP0 IN */
+ .ptx_fifosz = 384, /* room for 7 IN EPs */
+
+#ifndef USB_DW_ARCH_SLAVE
+ .ahb_burst_len = HBSTLEN_INCR16,
+ /* Disable Rx FIFO thresholding. It appears to cause problems,
+ * apparently a known issue -- Synopsys recommends disabling it
+ * because it can cause issues during certain error conditions.
+ */
+ .ahb_threshold = 0,
+#else
+ .disable_double_buffering = false,
+#endif
+};
+
+/* USB PHY init from Linux kernel code:
+ * - arch/mips/xburst/soc-x1000/common/cpm_usb.c
+ * Copyright (C) 2005-2017 Ingenic Semiconductor
+ */
+void usb_dw_target_enable_clocks(void)
+{
+ /* Enable CPM clock */
+ jz_writef(CPM_CLKGR, OTG(0));
+#if X1000_EXCLK_FREQ == 24000000
+ jz_writef(CPM_USBCDR, CLKSRC_V(EXCLK), CE(1), CLKDIV(0), PHY_GATE(0));
+#else
+# error "please add USB clock settings for 26 MHz EXCLK"
+#endif
+ while(jz_readf(CPM_USBCDR, BUSY));
+ jz_writef(CPM_USBCDR, CE(0));
+
+ /* PHY soft reset */
+ jz_writef(CPM_SRBC, OTG_SR(1));
+ udelay(10);
+ jz_writef(CPM_SRBC, OTG_SR(0));
+
+ /* Ungate PHY clock */
+ jz_writef(CPM_OPCR, GATE_USBPHY_CLK(0));
+
+ /* Exit suspend state */
+ jz_writef(CPM_OPCR, SPENDN0(1));
+ udelay(45);
+
+ /* Program core configuration */
+ jz_overwritef(CPM_USBVBFIL,
+ IDDIGFIL(0),
+ VBFIL(0));
+ jz_overwritef(CPM_USBRDT,
+ HB_MASK(0),
+ VBFIL_LD_EN(1),
+ IDDIG_EN(0),
+ RDT(0x96));
+ jz_overwritef(CPM_USBPCR,
+ OTG_DISABLE(1),
+ COMMONONN(1),
+ VBUSVLDEXT(1),
+ VBUSVLDEXTSEL(1),
+ SQRXTUNE(7),
+ TXPREEMPHTUNE(1),
+ TXHSXVTUNE(1),
+ TXVREFTUNE(7));
+ jz_overwritef(CPM_USBPCR1,
+ BVLD_REG(1),
+ REFCLK_SEL_V(CLKCORE),
+ REFCLK_DIV_V(24MHZ), /* applies for 26 MHz EXCLK too */
+ WORD_IF_V(16BIT));
+
+ /* Power on reset */
+ jz_writef(CPM_USBPCR, POR(1));
+ mdelay(1);
+ jz_writef(CPM_USBPCR, POR(0));
+ mdelay(1);
+}
+
+void usb_dw_target_disable_clocks(void)
+{
+ /* Suspend and power down PHY, then gate its clock */
+ jz_writef(CPM_OPCR, SPENDN0(0));
+ udelay(5);
+ jz_writef(CPM_USBPCR, OTG_DISABLE(1), SIDDQ(1));
+ jz_writef(CPM_OPCR, GATE_USBPHY_CLK(1));
+
+ /* Disable CPM clock */
+ jz_writef(CPM_USBCDR, CE(1), STOP(1), PHY_GATE(1));
+ while(jz_readf(CPM_USBCDR, BUSY));
+ jz_writef(CPM_USBCDR, CE(0));
+ jz_writef(CPM_CLKGR, OTG(1));
+}
+
+void usb_dw_target_enable_irq(void)
+{
+ system_enable_irq(IRQ_OTG);
+}
+
+void usb_dw_target_disable_irq(void)
+{
+ system_disable_irq(IRQ_OTG);
+}
+
+void usb_dw_target_clear_irq(void)
+{
+}
+
+/*
+ * Rockbox API
+ */
+
+#ifdef USB_STATUS_BY_EVENT
+static volatile int usb_status = USB_EXTRACTED;
+static void usb_detect_interrupt(void);
+#endif
+
+static int __usb_detect(void)
+{
+ /* XXX: Do we need an active level define for this? */
+ if(gpio_get_level(GPIO_USB_DETECT))
+ return USB_INSERTED;
+ else
+ return USB_EXTRACTED;
+}
+
+void usb_enable(bool on)
+{
+ if(on)
+ usb_core_init();
+ else
+ usb_core_exit();
+}
+
+void usb_init_device(void)
+{
+ /* Disable drvvbus pin -- it is only used when acting as a host,
+ * which Rockbox does not support */
+ gpio_set_function(GPIO_USB_DRVVBUS, GPIOF_OUTPUT(0));
+
+ /* Power up the core clocks to allow writing
+ to some registers needed to power it down */
+ usb_dw_target_disable_irq();
+ usb_dw_target_enable_clocks();
+ usb_drv_exit();
+
+#ifdef USB_STATUS_BY_EVENT
+ /* Setup USB detect pin IRQ */
+ usb_status = __usb_detect();
+ system_set_irq_handler(GPIO_TO_IRQ(GPIO_USB_DETECT), usb_detect_interrupt);
+ gpio_set_function(GPIO_USB_DETECT, GPIOF_IRQ_EDGE(1));
+ gpio_flip_edge_irq(GPIO_USB_DETECT);
+ gpio_enable_irq(GPIO_USB_DETECT);
+#endif
+}
+
+#ifndef USB_STATUS_BY_EVENT
+int usb_detect(void)
+{
+ return __usb_detect();
+}
+#else
+int usb_detect(void)
+{
+ return usb_status;
+}
+
+static void usb_detect_interrupt(void)
+{
+ /* Update status and flip the IRQ trigger edge */
+ usb_status = __usb_detect();
+ gpio_flip_edge_irq(GPIO_USB_DETECT);
+
+ /* Notify Rockbox of event */
+ usb_status_event(usb_status);
+}
+#endif
diff --git a/firmware/target/mips/ingenic_x1000/x1000/aic.h b/firmware/target/mips/ingenic_x1000/x1000/aic.h
index e9c68511d7..5f5e771c2c 100644
--- a/firmware/target/mips/ingenic_x1000/x1000/aic.h
+++ b/firmware/target/mips/ingenic_x1000/x1000/aic.h
@@ -123,18 +123,30 @@
#define JI_AIC_CCR
#define BP_AIC_CCR_CHANNEL 24
#define BM_AIC_CCR_CHANNEL 0x7000000
+#define BV_AIC_CCR_CHANNEL__MONO 0x0
+#define BV_AIC_CCR_CHANNEL__STEREO 0x1
#define BF_AIC_CCR_CHANNEL(v) (((v) & 0x7) << 24)
#define BFM_AIC_CCR_CHANNEL(v) BM_AIC_CCR_CHANNEL
#define BF_AIC_CCR_CHANNEL_V(e) BF_AIC_CCR_CHANNEL(BV_AIC_CCR_CHANNEL__##e)
#define BFM_AIC_CCR_CHANNEL_V(v) BM_AIC_CCR_CHANNEL
#define BP_AIC_CCR_OSS 19
#define BM_AIC_CCR_OSS 0x380000
+#define BV_AIC_CCR_OSS__8BIT 0x0
+#define BV_AIC_CCR_OSS__16BIT 0x1
+#define BV_AIC_CCR_OSS__18BIT 0x2
+#define BV_AIC_CCR_OSS__20BIT 0x3
+#define BV_AIC_CCR_OSS__24BIT 0x4
#define BF_AIC_CCR_OSS(v) (((v) & 0x7) << 19)
#define BFM_AIC_CCR_OSS(v) BM_AIC_CCR_OSS
#define BF_AIC_CCR_OSS_V(e) BF_AIC_CCR_OSS(BV_AIC_CCR_OSS__##e)
#define BFM_AIC_CCR_OSS_V(v) BM_AIC_CCR_OSS
#define BP_AIC_CCR_ISS 16
#define BM_AIC_CCR_ISS 0x70000
+#define BV_AIC_CCR_ISS__8BIT 0x0
+#define BV_AIC_CCR_ISS__16BIT 0x1
+#define BV_AIC_CCR_ISS__18BIT 0x2
+#define BV_AIC_CCR_ISS__20BIT 0x3
+#define BV_AIC_CCR_ISS__24BIT 0x4
#define BF_AIC_CCR_ISS(v) (((v) & 0x7) << 16)
#define BFM_AIC_CCR_ISS(v) BM_AIC_CCR_ISS
#define BF_AIC_CCR_ISS_V(e) BF_AIC_CCR_ISS(BV_AIC_CCR_ISS__##e)
@@ -356,4 +368,262 @@
#define JN_AIC_DR AIC_DR
#define JI_AIC_DR
+#define REG_AIC_SPENA jz_reg(AIC_SPENA)
+#define JA_AIC_SPENA (0xb0020000 + 0x80)
+#define JT_AIC_SPENA JIO_32_RW
+#define JN_AIC_SPENA AIC_SPENA
+#define JI_AIC_SPENA
+
+#define REG_AIC_SPCTRL jz_reg(AIC_SPCTRL)
+#define JA_AIC_SPCTRL (0xb0020000 + 0x84)
+#define JT_AIC_SPCTRL JIO_32_RW
+#define JN_AIC_SPCTRL AIC_SPCTRL
+#define JI_AIC_SPCTRL
+#define BP_AIC_SPCTRL_DMA_EN 15
+#define BM_AIC_SPCTRL_DMA_EN 0x8000
+#define BF_AIC_SPCTRL_DMA_EN(v) (((v) & 0x1) << 15)
+#define BFM_AIC_SPCTRL_DMA_EN(v) BM_AIC_SPCTRL_DMA_EN
+#define BF_AIC_SPCTRL_DMA_EN_V(e) BF_AIC_SPCTRL_DMA_EN(BV_AIC_SPCTRL_DMA_EN__##e)
+#define BFM_AIC_SPCTRL_DMA_EN_V(v) BM_AIC_SPCTRL_DMA_EN
+#define BP_AIC_SPCTRL_D_TYPE 14
+#define BM_AIC_SPCTRL_D_TYPE 0x4000
+#define BF_AIC_SPCTRL_D_TYPE(v) (((v) & 0x1) << 14)
+#define BFM_AIC_SPCTRL_D_TYPE(v) BM_AIC_SPCTRL_D_TYPE
+#define BF_AIC_SPCTRL_D_TYPE_V(e) BF_AIC_SPCTRL_D_TYPE(BV_AIC_SPCTRL_D_TYPE__##e)
+#define BFM_AIC_SPCTRL_D_TYPE_V(v) BM_AIC_SPCTRL_D_TYPE
+#define BP_AIC_SPCTRL_SIGN_N 13
+#define BM_AIC_SPCTRL_SIGN_N 0x2000
+#define BF_AIC_SPCTRL_SIGN_N(v) (((v) & 0x1) << 13)
+#define BFM_AIC_SPCTRL_SIGN_N(v) BM_AIC_SPCTRL_SIGN_N
+#define BF_AIC_SPCTRL_SIGN_N_V(e) BF_AIC_SPCTRL_SIGN_N(BV_AIC_SPCTRL_SIGN_N__##e)
+#define BFM_AIC_SPCTRL_SIGN_N_V(v) BM_AIC_SPCTRL_SIGN_N
+#define BP_AIC_SPCTRL_INVALID 12
+#define BM_AIC_SPCTRL_INVALID 0x1000
+#define BF_AIC_SPCTRL_INVALID(v) (((v) & 0x1) << 12)
+#define BFM_AIC_SPCTRL_INVALID(v) BM_AIC_SPCTRL_INVALID
+#define BF_AIC_SPCTRL_INVALID_V(e) BF_AIC_SPCTRL_INVALID(BV_AIC_SPCTRL_INVALID__##e)
+#define BFM_AIC_SPCTRL_INVALID_V(v) BM_AIC_SPCTRL_INVALID
+#define BP_AIC_SPCTRL_SFT_RST 11
+#define BM_AIC_SPCTRL_SFT_RST 0x800
+#define BF_AIC_SPCTRL_SFT_RST(v) (((v) & 0x1) << 11)
+#define BFM_AIC_SPCTRL_SFT_RST(v) BM_AIC_SPCTRL_SFT_RST
+#define BF_AIC_SPCTRL_SFT_RST_V(e) BF_AIC_SPCTRL_SFT_RST(BV_AIC_SPCTRL_SFT_RST__##e)
+#define BFM_AIC_SPCTRL_SFT_RST_V(v) BM_AIC_SPCTRL_SFT_RST
+#define BP_AIC_SPCTRL_SPDIF_I2S 10
+#define BM_AIC_SPCTRL_SPDIF_I2S 0x400
+#define BF_AIC_SPCTRL_SPDIF_I2S(v) (((v) & 0x1) << 10)
+#define BFM_AIC_SPCTRL_SPDIF_I2S(v) BM_AIC_SPCTRL_SPDIF_I2S
+#define BF_AIC_SPCTRL_SPDIF_I2S_V(e) BF_AIC_SPCTRL_SPDIF_I2S(BV_AIC_SPCTRL_SPDIF_I2S__##e)
+#define BFM_AIC_SPCTRL_SPDIF_I2S_V(v) BM_AIC_SPCTRL_SPDIF_I2S
+#define BP_AIC_SPCTRL_M_TRIG 1
+#define BM_AIC_SPCTRL_M_TRIG 0x2
+#define BF_AIC_SPCTRL_M_TRIG(v) (((v) & 0x1) << 1)
+#define BFM_AIC_SPCTRL_M_TRIG(v) BM_AIC_SPCTRL_M_TRIG
+#define BF_AIC_SPCTRL_M_TRIG_V(e) BF_AIC_SPCTRL_M_TRIG(BV_AIC_SPCTRL_M_TRIG__##e)
+#define BFM_AIC_SPCTRL_M_TRIG_V(v) BM_AIC_SPCTRL_M_TRIG
+#define BP_AIC_SPCTRL_M_FFUR 0
+#define BM_AIC_SPCTRL_M_FFUR 0x1
+#define BF_AIC_SPCTRL_M_FFUR(v) (((v) & 0x1) << 0)
+#define BFM_AIC_SPCTRL_M_FFUR(v) BM_AIC_SPCTRL_M_FFUR
+#define BF_AIC_SPCTRL_M_FFUR_V(e) BF_AIC_SPCTRL_M_FFUR(BV_AIC_SPCTRL_M_FFUR__##e)
+#define BFM_AIC_SPCTRL_M_FFUR_V(v) BM_AIC_SPCTRL_M_FFUR
+
+#define REG_AIC_SPSTATE jz_reg(AIC_SPSTATE)
+#define JA_AIC_SPSTATE (0xb0020000 + 0x88)
+#define JT_AIC_SPSTATE JIO_32_RW
+#define JN_AIC_SPSTATE AIC_SPSTATE
+#define JI_AIC_SPSTATE
+#define BP_AIC_SPSTATE_FIFO_LEVEL 8
+#define BM_AIC_SPSTATE_FIFO_LEVEL 0x7f00
+#define BF_AIC_SPSTATE_FIFO_LEVEL(v) (((v) & 0x7f) << 8)
+#define BFM_AIC_SPSTATE_FIFO_LEVEL(v) BM_AIC_SPSTATE_FIFO_LEVEL
+#define BF_AIC_SPSTATE_FIFO_LEVEL_V(e) BF_AIC_SPSTATE_FIFO_LEVEL(BV_AIC_SPSTATE_FIFO_LEVEL__##e)
+#define BFM_AIC_SPSTATE_FIFO_LEVEL_V(v) BM_AIC_SPSTATE_FIFO_LEVEL
+#define BP_AIC_SPSTATE_BUSY 7
+#define BM_AIC_SPSTATE_BUSY 0x80
+#define BF_AIC_SPSTATE_BUSY(v) (((v) & 0x1) << 7)
+#define BFM_AIC_SPSTATE_BUSY(v) BM_AIC_SPSTATE_BUSY
+#define BF_AIC_SPSTATE_BUSY_V(e) BF_AIC_SPSTATE_BUSY(BV_AIC_SPSTATE_BUSY__##e)
+#define BFM_AIC_SPSTATE_BUSY_V(v) BM_AIC_SPSTATE_BUSY
+#define BP_AIC_SPSTATE_F_TRIG 1
+#define BM_AIC_SPSTATE_F_TRIG 0x2
+#define BF_AIC_SPSTATE_F_TRIG(v) (((v) & 0x1) << 1)
+#define BFM_AIC_SPSTATE_F_TRIG(v) BM_AIC_SPSTATE_F_TRIG
+#define BF_AIC_SPSTATE_F_TRIG_V(e) BF_AIC_SPSTATE_F_TRIG(BV_AIC_SPSTATE_F_TRIG__##e)
+#define BFM_AIC_SPSTATE_F_TRIG_V(v) BM_AIC_SPSTATE_F_TRIG
+#define BP_AIC_SPSTATE_F_FFUR 0
+#define BM_AIC_SPSTATE_F_FFUR 0x1
+#define BF_AIC_SPSTATE_F_FFUR(v) (((v) & 0x1) << 0)
+#define BFM_AIC_SPSTATE_F_FFUR(v) BM_AIC_SPSTATE_F_FFUR
+#define BF_AIC_SPSTATE_F_FFUR_V(e) BF_AIC_SPSTATE_F_FFUR(BV_AIC_SPSTATE_F_FFUR__##e)
+#define BFM_AIC_SPSTATE_F_FFUR_V(v) BM_AIC_SPSTATE_F_FFUR
+
+#define REG_AIC_SPCFG1 jz_reg(AIC_SPCFG1)
+#define JA_AIC_SPCFG1 (0xb0020000 + 0x8c)
+#define JT_AIC_SPCFG1 JIO_32_RW
+#define JN_AIC_SPCFG1 AIC_SPCFG1
+#define JI_AIC_SPCFG1
+#define BP_AIC_SPCFG1_TRIG 12
+#define BM_AIC_SPCFG1_TRIG 0x3000
+#define BF_AIC_SPCFG1_TRIG(v) (((v) & 0x3) << 12)
+#define BFM_AIC_SPCFG1_TRIG(v) BM_AIC_SPCFG1_TRIG
+#define BF_AIC_SPCFG1_TRIG_V(e) BF_AIC_SPCFG1_TRIG(BV_AIC_SPCFG1_TRIG__##e)
+#define BFM_AIC_SPCFG1_TRIG_V(v) BM_AIC_SPCFG1_TRIG
+#define BP_AIC_SPCFG1_SRC_NUM 8
+#define BM_AIC_SPCFG1_SRC_NUM 0xf00
+#define BF_AIC_SPCFG1_SRC_NUM(v) (((v) & 0xf) << 8)
+#define BFM_AIC_SPCFG1_SRC_NUM(v) BM_AIC_SPCFG1_SRC_NUM
+#define BF_AIC_SPCFG1_SRC_NUM_V(e) BF_AIC_SPCFG1_SRC_NUM(BV_AIC_SPCFG1_SRC_NUM__##e)
+#define BFM_AIC_SPCFG1_SRC_NUM_V(v) BM_AIC_SPCFG1_SRC_NUM
+#define BP_AIC_SPCFG1_CH1_NUM 4
+#define BM_AIC_SPCFG1_CH1_NUM 0xf0
+#define BF_AIC_SPCFG1_CH1_NUM(v) (((v) & 0xf) << 4)
+#define BFM_AIC_SPCFG1_CH1_NUM(v) BM_AIC_SPCFG1_CH1_NUM
+#define BF_AIC_SPCFG1_CH1_NUM_V(e) BF_AIC_SPCFG1_CH1_NUM(BV_AIC_SPCFG1_CH1_NUM__##e)
+#define BFM_AIC_SPCFG1_CH1_NUM_V(v) BM_AIC_SPCFG1_CH1_NUM
+#define BP_AIC_SPCFG1_CH2_NUM 0
+#define BM_AIC_SPCFG1_CH2_NUM 0xf
+#define BF_AIC_SPCFG1_CH2_NUM(v) (((v) & 0xf) << 0)
+#define BFM_AIC_SPCFG1_CH2_NUM(v) BM_AIC_SPCFG1_CH2_NUM
+#define BF_AIC_SPCFG1_CH2_NUM_V(e) BF_AIC_SPCFG1_CH2_NUM(BV_AIC_SPCFG1_CH2_NUM__##e)
+#define BFM_AIC_SPCFG1_CH2_NUM_V(v) BM_AIC_SPCFG1_CH2_NUM
+#define BP_AIC_SPCFG1_INIT_LEVEL 17
+#define BM_AIC_SPCFG1_INIT_LEVEL 0x20000
+#define BF_AIC_SPCFG1_INIT_LEVEL(v) (((v) & 0x1) << 17)
+#define BFM_AIC_SPCFG1_INIT_LEVEL(v) BM_AIC_SPCFG1_INIT_LEVEL
+#define BF_AIC_SPCFG1_INIT_LEVEL_V(e) BF_AIC_SPCFG1_INIT_LEVEL(BV_AIC_SPCFG1_INIT_LEVEL__##e)
+#define BFM_AIC_SPCFG1_INIT_LEVEL_V(v) BM_AIC_SPCFG1_INIT_LEVEL
+#define BP_AIC_SPCFG1_ZERO_VALID 16
+#define BM_AIC_SPCFG1_ZERO_VALID 0x10000
+#define BF_AIC_SPCFG1_ZERO_VALID(v) (((v) & 0x1) << 16)
+#define BFM_AIC_SPCFG1_ZERO_VALID(v) BM_AIC_SPCFG1_ZERO_VALID
+#define BF_AIC_SPCFG1_ZERO_VALID_V(e) BF_AIC_SPCFG1_ZERO_VALID(BV_AIC_SPCFG1_ZERO_VALID__##e)
+#define BFM_AIC_SPCFG1_ZERO_VALID_V(v) BM_AIC_SPCFG1_ZERO_VALID
+
+#define REG_AIC_SPCFG2 jz_reg(AIC_SPCFG2)
+#define JA_AIC_SPCFG2 (0xb0020000 + 0x90)
+#define JT_AIC_SPCFG2 JIO_32_RW
+#define JN_AIC_SPCFG2 AIC_SPCFG2
+#define JI_AIC_SPCFG2
+#define BP_AIC_SPCFG2_FS 26
+#define BM_AIC_SPCFG2_FS 0x3c000000
+#define BF_AIC_SPCFG2_FS(v) (((v) & 0xf) << 26)
+#define BFM_AIC_SPCFG2_FS(v) BM_AIC_SPCFG2_FS
+#define BF_AIC_SPCFG2_FS_V(e) BF_AIC_SPCFG2_FS(BV_AIC_SPCFG2_FS__##e)
+#define BFM_AIC_SPCFG2_FS_V(v) BM_AIC_SPCFG2_FS
+#define BP_AIC_SPCFG2_ORG_FRQ 22
+#define BM_AIC_SPCFG2_ORG_FRQ 0x3c00000
+#define BF_AIC_SPCFG2_ORG_FRQ(v) (((v) & 0xf) << 22)
+#define BFM_AIC_SPCFG2_ORG_FRQ(v) BM_AIC_SPCFG2_ORG_FRQ
+#define BF_AIC_SPCFG2_ORG_FRQ_V(e) BF_AIC_SPCFG2_ORG_FRQ(BV_AIC_SPCFG2_ORG_FRQ__##e)
+#define BFM_AIC_SPCFG2_ORG_FRQ_V(v) BM_AIC_SPCFG2_ORG_FRQ
+#define BP_AIC_SPCFG2_SAMPL_WL 19
+#define BM_AIC_SPCFG2_SAMPL_WL 0x380000
+#define BF_AIC_SPCFG2_SAMPL_WL(v) (((v) & 0x7) << 19)
+#define BFM_AIC_SPCFG2_SAMPL_WL(v) BM_AIC_SPCFG2_SAMPL_WL
+#define BF_AIC_SPCFG2_SAMPL_WL_V(e) BF_AIC_SPCFG2_SAMPL_WL(BV_AIC_SPCFG2_SAMPL_WL__##e)
+#define BFM_AIC_SPCFG2_SAMPL_WL_V(v) BM_AIC_SPCFG2_SAMPL_WL
+#define BP_AIC_SPCFG2_CLK_ACU 16
+#define BM_AIC_SPCFG2_CLK_ACU 0x30000
+#define BF_AIC_SPCFG2_CLK_ACU(v) (((v) & 0x3) << 16)
+#define BFM_AIC_SPCFG2_CLK_ACU(v) BM_AIC_SPCFG2_CLK_ACU
+#define BF_AIC_SPCFG2_CLK_ACU_V(e) BF_AIC_SPCFG2_CLK_ACU(BV_AIC_SPCFG2_CLK_ACU__##e)
+#define BFM_AIC_SPCFG2_CLK_ACU_V(v) BM_AIC_SPCFG2_CLK_ACU
+#define BP_AIC_SPCFG2_CAT_CODE 8
+#define BM_AIC_SPCFG2_CAT_CODE 0xff00
+#define BF_AIC_SPCFG2_CAT_CODE(v) (((v) & 0xff) << 8)
+#define BFM_AIC_SPCFG2_CAT_CODE(v) BM_AIC_SPCFG2_CAT_CODE
+#define BF_AIC_SPCFG2_CAT_CODE_V(e) BF_AIC_SPCFG2_CAT_CODE(BV_AIC_SPCFG2_CAT_CODE__##e)
+#define BFM_AIC_SPCFG2_CAT_CODE_V(v) BM_AIC_SPCFG2_CAT_CODE
+#define BP_AIC_SPCFG2_CH_MD 6
+#define BM_AIC_SPCFG2_CH_MD 0xc0
+#define BF_AIC_SPCFG2_CH_MD(v) (((v) & 0x3) << 6)
+#define BFM_AIC_SPCFG2_CH_MD(v) BM_AIC_SPCFG2_CH_MD
+#define BF_AIC_SPCFG2_CH_MD_V(e) BF_AIC_SPCFG2_CH_MD(BV_AIC_SPCFG2_CH_MD__##e)
+#define BFM_AIC_SPCFG2_CH_MD_V(v) BM_AIC_SPCFG2_CH_MD
+#define BP_AIC_SPCFG2_MAX_WL 18
+#define BM_AIC_SPCFG2_MAX_WL 0x40000
+#define BF_AIC_SPCFG2_MAX_WL(v) (((v) & 0x1) << 18)
+#define BFM_AIC_SPCFG2_MAX_WL(v) BM_AIC_SPCFG2_MAX_WL
+#define BF_AIC_SPCFG2_MAX_WL_V(e) BF_AIC_SPCFG2_MAX_WL(BV_AIC_SPCFG2_MAX_WL__##e)
+#define BFM_AIC_SPCFG2_MAX_WL_V(v) BM_AIC_SPCFG2_MAX_WL
+#define BP_AIC_SPCFG2_PRE 3
+#define BM_AIC_SPCFG2_PRE 0x8
+#define BF_AIC_SPCFG2_PRE(v) (((v) & 0x1) << 3)
+#define BFM_AIC_SPCFG2_PRE(v) BM_AIC_SPCFG2_PRE
+#define BF_AIC_SPCFG2_PRE_V(e) BF_AIC_SPCFG2_PRE(BV_AIC_SPCFG2_PRE__##e)
+#define BFM_AIC_SPCFG2_PRE_V(v) BM_AIC_SPCFG2_PRE
+#define BP_AIC_SPCFG2_COPY_N 2
+#define BM_AIC_SPCFG2_COPY_N 0x4
+#define BF_AIC_SPCFG2_COPY_N(v) (((v) & 0x1) << 2)
+#define BFM_AIC_SPCFG2_COPY_N(v) BM_AIC_SPCFG2_COPY_N
+#define BF_AIC_SPCFG2_COPY_N_V(e) BF_AIC_SPCFG2_COPY_N(BV_AIC_SPCFG2_COPY_N__##e)
+#define BFM_AIC_SPCFG2_COPY_N_V(v) BM_AIC_SPCFG2_COPY_N
+#define BP_AIC_SPCFG2_AUDIO_N 1
+#define BM_AIC_SPCFG2_AUDIO_N 0x2
+#define BF_AIC_SPCFG2_AUDIO_N(v) (((v) & 0x1) << 1)
+#define BFM_AIC_SPCFG2_AUDIO_N(v) BM_AIC_SPCFG2_AUDIO_N
+#define BF_AIC_SPCFG2_AUDIO_N_V(e) BF_AIC_SPCFG2_AUDIO_N(BV_AIC_SPCFG2_AUDIO_N__##e)
+#define BFM_AIC_SPCFG2_AUDIO_N_V(v) BM_AIC_SPCFG2_AUDIO_N
+#define BP_AIC_SPCFG2_CON_PRO 0
+#define BM_AIC_SPCFG2_CON_PRO 0x1
+#define BF_AIC_SPCFG2_CON_PRO(v) (((v) & 0x1) << 0)
+#define BFM_AIC_SPCFG2_CON_PRO(v) BM_AIC_SPCFG2_CON_PRO
+#define BF_AIC_SPCFG2_CON_PRO_V(e) BF_AIC_SPCFG2_CON_PRO(BV_AIC_SPCFG2_CON_PRO__##e)
+#define BFM_AIC_SPCFG2_CON_PRO_V(v) BM_AIC_SPCFG2_CON_PRO
+
+#define REG_AIC_SPFIFO jz_reg(AIC_SPFIFO)
+#define JA_AIC_SPFIFO (0xb0020000 + 0x94)
+#define JT_AIC_SPFIFO JIO_32_RW
+#define JN_AIC_SPFIFO AIC_SPFIFO
+#define JI_AIC_SPFIFO
+
+#define REG_AIC_RGADW jz_reg(AIC_RGADW)
+#define JA_AIC_RGADW (0xb0020000 + 0xa4)
+#define JT_AIC_RGADW JIO_32_RW
+#define JN_AIC_RGADW AIC_RGADW
+#define JI_AIC_RGADW
+#define BP_AIC_RGADW_ADDR 8
+#define BM_AIC_RGADW_ADDR 0x7f00
+#define BF_AIC_RGADW_ADDR(v) (((v) & 0x7f) << 8)
+#define BFM_AIC_RGADW_ADDR(v) BM_AIC_RGADW_ADDR
+#define BF_AIC_RGADW_ADDR_V(e) BF_AIC_RGADW_ADDR(BV_AIC_RGADW_ADDR__##e)
+#define BFM_AIC_RGADW_ADDR_V(v) BM_AIC_RGADW_ADDR
+#define BP_AIC_RGADW_DATA 0
+#define BM_AIC_RGADW_DATA 0xff
+#define BF_AIC_RGADW_DATA(v) (((v) & 0xff) << 0)
+#define BFM_AIC_RGADW_DATA(v) BM_AIC_RGADW_DATA
+#define BF_AIC_RGADW_DATA_V(e) BF_AIC_RGADW_DATA(BV_AIC_RGADW_DATA__##e)
+#define BFM_AIC_RGADW_DATA_V(v) BM_AIC_RGADW_DATA
+#define BP_AIC_RGADW_ICRST 31
+#define BM_AIC_RGADW_ICRST 0x80000000
+#define BF_AIC_RGADW_ICRST(v) (((v) & 0x1) << 31)
+#define BFM_AIC_RGADW_ICRST(v) BM_AIC_RGADW_ICRST
+#define BF_AIC_RGADW_ICRST_V(e) BF_AIC_RGADW_ICRST(BV_AIC_RGADW_ICRST__##e)
+#define BFM_AIC_RGADW_ICRST_V(v) BM_AIC_RGADW_ICRST
+#define BP_AIC_RGADW_RGWR 16
+#define BM_AIC_RGADW_RGWR 0x10000
+#define BF_AIC_RGADW_RGWR(v) (((v) & 0x1) << 16)
+#define BFM_AIC_RGADW_RGWR(v) BM_AIC_RGADW_RGWR
+#define BF_AIC_RGADW_RGWR_V(e) BF_AIC_RGADW_RGWR(BV_AIC_RGADW_RGWR__##e)
+#define BFM_AIC_RGADW_RGWR_V(v) BM_AIC_RGADW_RGWR
+
+#define REG_AIC_RGDATA jz_reg(AIC_RGDATA)
+#define JA_AIC_RGDATA (0xb0020000 + 0xa8)
+#define JT_AIC_RGDATA JIO_32_RW
+#define JN_AIC_RGDATA AIC_RGDATA
+#define JI_AIC_RGDATA
+#define BP_AIC_RGDATA_DATA 0
+#define BM_AIC_RGDATA_DATA 0xff
+#define BF_AIC_RGDATA_DATA(v) (((v) & 0xff) << 0)
+#define BFM_AIC_RGDATA_DATA(v) BM_AIC_RGDATA_DATA
+#define BF_AIC_RGDATA_DATA_V(e) BF_AIC_RGDATA_DATA(BV_AIC_RGDATA_DATA__##e)
+#define BFM_AIC_RGDATA_DATA_V(v) BM_AIC_RGDATA_DATA
+#define BP_AIC_RGDATA_IRQ 8
+#define BM_AIC_RGDATA_IRQ 0x100
+#define BF_AIC_RGDATA_IRQ(v) (((v) & 0x1) << 8)
+#define BFM_AIC_RGDATA_IRQ(v) BM_AIC_RGDATA_IRQ
+#define BF_AIC_RGDATA_IRQ_V(e) BF_AIC_RGDATA_IRQ(BV_AIC_RGDATA_IRQ__##e)
+#define BFM_AIC_RGDATA_IRQ_V(v) BM_AIC_RGDATA_IRQ
+
#endif /* __HEADERGEN_AIC_H__*/
diff --git a/firmware/target/mips/ingenic_x1000/x1000/cpm.h b/firmware/target/mips/ingenic_x1000/x1000/cpm.h
index 752d270f20..3d59fd6fc7 100644
--- a/firmware/target/mips/ingenic_x1000/x1000/cpm.h
+++ b/firmware/target/mips/ingenic_x1000/x1000/cpm.h
@@ -233,6 +233,44 @@
#define BF_CPM_DDRCDR_FLAG_V(e) BF_CPM_DDRCDR_FLAG(BV_CPM_DDRCDR_FLAG__##e)
#define BFM_CPM_DDRCDR_FLAG_V(v) BM_CPM_DDRCDR_FLAG
+#define REG_CPM_MACCDR jz_reg(CPM_MACCDR)
+#define JA_CPM_MACCDR (0xb0000000 + 0x54)
+#define JT_CPM_MACCDR JIO_32_RW
+#define JN_CPM_MACCDR CPM_MACCDR
+#define JI_CPM_MACCDR
+#define BP_CPM_MACCDR_CLKDIV 0
+#define BM_CPM_MACCDR_CLKDIV 0xff
+#define BF_CPM_MACCDR_CLKDIV(v) (((v) & 0xff) << 0)
+#define BFM_CPM_MACCDR_CLKDIV(v) BM_CPM_MACCDR_CLKDIV
+#define BF_CPM_MACCDR_CLKDIV_V(e) BF_CPM_MACCDR_CLKDIV(BV_CPM_MACCDR_CLKDIV__##e)
+#define BFM_CPM_MACCDR_CLKDIV_V(v) BM_CPM_MACCDR_CLKDIV
+#define BP_CPM_MACCDR_CLKSRC 31
+#define BM_CPM_MACCDR_CLKSRC 0x80000000
+#define BV_CPM_MACCDR_CLKSRC__SCLK_A 0x0
+#define BV_CPM_MACCDR_CLKSRC__MPLL 0x1
+#define BF_CPM_MACCDR_CLKSRC(v) (((v) & 0x1) << 31)
+#define BFM_CPM_MACCDR_CLKSRC(v) BM_CPM_MACCDR_CLKSRC
+#define BF_CPM_MACCDR_CLKSRC_V(e) BF_CPM_MACCDR_CLKSRC(BV_CPM_MACCDR_CLKSRC__##e)
+#define BFM_CPM_MACCDR_CLKSRC_V(v) BM_CPM_MACCDR_CLKSRC
+#define BP_CPM_MACCDR_CE 29
+#define BM_CPM_MACCDR_CE 0x20000000
+#define BF_CPM_MACCDR_CE(v) (((v) & 0x1) << 29)
+#define BFM_CPM_MACCDR_CE(v) BM_CPM_MACCDR_CE
+#define BF_CPM_MACCDR_CE_V(e) BF_CPM_MACCDR_CE(BV_CPM_MACCDR_CE__##e)
+#define BFM_CPM_MACCDR_CE_V(v) BM_CPM_MACCDR_CE
+#define BP_CPM_MACCDR_BUSY 28
+#define BM_CPM_MACCDR_BUSY 0x10000000
+#define BF_CPM_MACCDR_BUSY(v) (((v) & 0x1) << 28)
+#define BFM_CPM_MACCDR_BUSY(v) BM_CPM_MACCDR_BUSY
+#define BF_CPM_MACCDR_BUSY_V(e) BF_CPM_MACCDR_BUSY(BV_CPM_MACCDR_BUSY__##e)
+#define BFM_CPM_MACCDR_BUSY_V(v) BM_CPM_MACCDR_BUSY
+#define BP_CPM_MACCDR_STOP 27
+#define BM_CPM_MACCDR_STOP 0x8000000
+#define BF_CPM_MACCDR_STOP(v) (((v) & 0x1) << 27)
+#define BFM_CPM_MACCDR_STOP(v) BM_CPM_MACCDR_STOP
+#define BF_CPM_MACCDR_STOP_V(e) BF_CPM_MACCDR_STOP(BV_CPM_MACCDR_STOP__##e)
+#define BFM_CPM_MACCDR_STOP_V(v) BM_CPM_MACCDR_STOP
+
#define REG_CPM_I2SCDR jz_reg(CPM_I2SCDR)
#define JA_CPM_I2SCDR (0xb0000000 + 0x60)
#define JT_CPM_I2SCDR JIO_32_RW
@@ -401,6 +439,51 @@
#define BF_CPM_MSC1CDR_S_CLK1_SEL_V(e) BF_CPM_MSC1CDR_S_CLK1_SEL(BV_CPM_MSC1CDR_S_CLK1_SEL__##e)
#define BFM_CPM_MSC1CDR_S_CLK1_SEL_V(v) BM_CPM_MSC1CDR_S_CLK1_SEL
+#define REG_CPM_USBCDR jz_reg(CPM_USBCDR)
+#define JA_CPM_USBCDR (0xb0000000 + 0x50)
+#define JT_CPM_USBCDR JIO_32_RW
+#define JN_CPM_USBCDR CPM_USBCDR
+#define JI_CPM_USBCDR
+#define BP_CPM_USBCDR_CLKSRC 30
+#define BM_CPM_USBCDR_CLKSRC 0xc0000000
+#define BV_CPM_USBCDR_CLKSRC__EXCLK 0x0
+#define BV_CPM_USBCDR_CLKSRC__SCLK_A 0x2
+#define BV_CPM_USBCDR_CLKSRC__MPLL 0x3
+#define BF_CPM_USBCDR_CLKSRC(v) (((v) & 0x3) << 30)
+#define BFM_CPM_USBCDR_CLKSRC(v) BM_CPM_USBCDR_CLKSRC
+#define BF_CPM_USBCDR_CLKSRC_V(e) BF_CPM_USBCDR_CLKSRC(BV_CPM_USBCDR_CLKSRC__##e)
+#define BFM_CPM_USBCDR_CLKSRC_V(v) BM_CPM_USBCDR_CLKSRC
+#define BP_CPM_USBCDR_CLKDIV 0
+#define BM_CPM_USBCDR_CLKDIV 0xff
+#define BF_CPM_USBCDR_CLKDIV(v) (((v) & 0xff) << 0)
+#define BFM_CPM_USBCDR_CLKDIV(v) BM_CPM_USBCDR_CLKDIV
+#define BF_CPM_USBCDR_CLKDIV_V(e) BF_CPM_USBCDR_CLKDIV(BV_CPM_USBCDR_CLKDIV__##e)
+#define BFM_CPM_USBCDR_CLKDIV_V(v) BM_CPM_USBCDR_CLKDIV
+#define BP_CPM_USBCDR_CE 29
+#define BM_CPM_USBCDR_CE 0x20000000
+#define BF_CPM_USBCDR_CE(v) (((v) & 0x1) << 29)
+#define BFM_CPM_USBCDR_CE(v) BM_CPM_USBCDR_CE
+#define BF_CPM_USBCDR_CE_V(e) BF_CPM_USBCDR_CE(BV_CPM_USBCDR_CE__##e)
+#define BFM_CPM_USBCDR_CE_V(v) BM_CPM_USBCDR_CE
+#define BP_CPM_USBCDR_BUSY 28
+#define BM_CPM_USBCDR_BUSY 0x10000000
+#define BF_CPM_USBCDR_BUSY(v) (((v) & 0x1) << 28)
+#define BFM_CPM_USBCDR_BUSY(v) BM_CPM_USBCDR_BUSY
+#define BF_CPM_USBCDR_BUSY_V(e) BF_CPM_USBCDR_BUSY(BV_CPM_USBCDR_BUSY__##e)
+#define BFM_CPM_USBCDR_BUSY_V(v) BM_CPM_USBCDR_BUSY
+#define BP_CPM_USBCDR_STOP 27
+#define BM_CPM_USBCDR_STOP 0x8000000
+#define BF_CPM_USBCDR_STOP(v) (((v) & 0x1) << 27)
+#define BFM_CPM_USBCDR_STOP(v) BM_CPM_USBCDR_STOP
+#define BF_CPM_USBCDR_STOP_V(e) BF_CPM_USBCDR_STOP(BV_CPM_USBCDR_STOP__##e)
+#define BFM_CPM_USBCDR_STOP_V(v) BM_CPM_USBCDR_STOP
+#define BP_CPM_USBCDR_PHY_GATE 26
+#define BM_CPM_USBCDR_PHY_GATE 0x4000000
+#define BF_CPM_USBCDR_PHY_GATE(v) (((v) & 0x1) << 26)
+#define BFM_CPM_USBCDR_PHY_GATE(v) BM_CPM_USBCDR_PHY_GATE
+#define BF_CPM_USBCDR_PHY_GATE_V(e) BF_CPM_USBCDR_PHY_GATE(BV_CPM_USBCDR_PHY_GATE__##e)
+#define BFM_CPM_USBCDR_PHY_GATE_V(v) BM_CPM_USBCDR_PHY_GATE
+
#define REG_CPM_SSICDR jz_reg(CPM_SSICDR)
#define JA_CPM_SSICDR (0xb0000000 + 0x74)
#define JT_CPM_SSICDR JIO_32_RW
@@ -447,12 +530,379 @@
#define BF_CPM_SSICDR_STOP_V(e) BF_CPM_SSICDR_STOP(BV_CPM_SSICDR_STOP__##e)
#define BFM_CPM_SSICDR_STOP_V(v) BM_CPM_SSICDR_STOP
+#define REG_CPM_CIMCDR jz_reg(CPM_CIMCDR)
+#define JA_CPM_CIMCDR (0xb0000000 + 0x7c)
+#define JT_CPM_CIMCDR JIO_32_RW
+#define JN_CPM_CIMCDR CPM_CIMCDR
+#define JI_CPM_CIMCDR
+#define BP_CPM_CIMCDR_CLKDIV 0
+#define BM_CPM_CIMCDR_CLKDIV 0xff
+#define BF_CPM_CIMCDR_CLKDIV(v) (((v) & 0xff) << 0)
+#define BFM_CPM_CIMCDR_CLKDIV(v) BM_CPM_CIMCDR_CLKDIV
+#define BF_CPM_CIMCDR_CLKDIV_V(e) BF_CPM_CIMCDR_CLKDIV(BV_CPM_CIMCDR_CLKDIV__##e)
+#define BFM_CPM_CIMCDR_CLKDIV_V(v) BM_CPM_CIMCDR_CLKDIV
+#define BP_CPM_CIMCDR_CLKSRC 31
+#define BM_CPM_CIMCDR_CLKSRC 0x80000000
+#define BV_CPM_CIMCDR_CLKSRC__SCLK_A 0x1
+#define BV_CPM_CIMCDR_CLKSRC__MPLL 0x1
+#define BF_CPM_CIMCDR_CLKSRC(v) (((v) & 0x1) << 31)
+#define BFM_CPM_CIMCDR_CLKSRC(v) BM_CPM_CIMCDR_CLKSRC
+#define BF_CPM_CIMCDR_CLKSRC_V(e) BF_CPM_CIMCDR_CLKSRC(BV_CPM_CIMCDR_CLKSRC__##e)
+#define BFM_CPM_CIMCDR_CLKSRC_V(v) BM_CPM_CIMCDR_CLKSRC
+#define BP_CPM_CIMCDR_CE 29
+#define BM_CPM_CIMCDR_CE 0x20000000
+#define BF_CPM_CIMCDR_CE(v) (((v) & 0x1) << 29)
+#define BFM_CPM_CIMCDR_CE(v) BM_CPM_CIMCDR_CE
+#define BF_CPM_CIMCDR_CE_V(e) BF_CPM_CIMCDR_CE(BV_CPM_CIMCDR_CE__##e)
+#define BFM_CPM_CIMCDR_CE_V(v) BM_CPM_CIMCDR_CE
+#define BP_CPM_CIMCDR_BUSY 28
+#define BM_CPM_CIMCDR_BUSY 0x10000000
+#define BF_CPM_CIMCDR_BUSY(v) (((v) & 0x1) << 28)
+#define BFM_CPM_CIMCDR_BUSY(v) BM_CPM_CIMCDR_BUSY
+#define BF_CPM_CIMCDR_BUSY_V(e) BF_CPM_CIMCDR_BUSY(BV_CPM_CIMCDR_BUSY__##e)
+#define BFM_CPM_CIMCDR_BUSY_V(v) BM_CPM_CIMCDR_BUSY
+#define BP_CPM_CIMCDR_STOP 27
+#define BM_CPM_CIMCDR_STOP 0x8000000
+#define BF_CPM_CIMCDR_STOP(v) (((v) & 0x1) << 27)
+#define BFM_CPM_CIMCDR_STOP(v) BM_CPM_CIMCDR_STOP
+#define BF_CPM_CIMCDR_STOP_V(e) BF_CPM_CIMCDR_STOP(BV_CPM_CIMCDR_STOP__##e)
+#define BFM_CPM_CIMCDR_STOP_V(v) BM_CPM_CIMCDR_STOP
+
+#define REG_CPM_PCMCDR jz_reg(CPM_PCMCDR)
+#define JA_CPM_PCMCDR (0xb0000000 + 0x84)
+#define JT_CPM_PCMCDR JIO_32_RW
+#define JN_CPM_PCMCDR CPM_PCMCDR
+#define JI_CPM_PCMCDR
+#define BP_CPM_PCMCDR_DIV_M 13
+#define BM_CPM_PCMCDR_DIV_M 0x3fe000
+#define BF_CPM_PCMCDR_DIV_M(v) (((v) & 0x1ff) << 13)
+#define BFM_CPM_PCMCDR_DIV_M(v) BM_CPM_PCMCDR_DIV_M
+#define BF_CPM_PCMCDR_DIV_M_V(e) BF_CPM_PCMCDR_DIV_M(BV_CPM_PCMCDR_DIV_M__##e)
+#define BFM_CPM_PCMCDR_DIV_M_V(v) BM_CPM_PCMCDR_DIV_M
+#define BP_CPM_PCMCDR_DIV_N 0
+#define BM_CPM_PCMCDR_DIV_N 0x1fff
+#define BF_CPM_PCMCDR_DIV_N(v) (((v) & 0x1fff) << 0)
+#define BFM_CPM_PCMCDR_DIV_N(v) BM_CPM_PCMCDR_DIV_N
+#define BF_CPM_PCMCDR_DIV_N_V(e) BF_CPM_PCMCDR_DIV_N(BV_CPM_PCMCDR_DIV_N__##e)
+#define BFM_CPM_PCMCDR_DIV_N_V(v) BM_CPM_PCMCDR_DIV_N
+#define BP_CPM_PCMCDR_PCS 31
+#define BM_CPM_PCMCDR_PCS 0x80000000
+#define BV_CPM_PCMCDR_PCS__SCLK_A 0x0
+#define BV_CPM_PCMCDR_PCS__MPLL 0x1
+#define BF_CPM_PCMCDR_PCS(v) (((v) & 0x1) << 31)
+#define BFM_CPM_PCMCDR_PCS(v) BM_CPM_PCMCDR_PCS
+#define BF_CPM_PCMCDR_PCS_V(e) BF_CPM_PCMCDR_PCS(BV_CPM_PCMCDR_PCS__##e)
+#define BFM_CPM_PCMCDR_PCS_V(v) BM_CPM_PCMCDR_PCS
+#define BP_CPM_PCMCDR_CS 30
+#define BM_CPM_PCMCDR_CS 0x40000000
+#define BV_CPM_PCMCDR_CS__EXCLK 0x0
+#define BV_CPM_PCMCDR_CS__PLL 0x1
+#define BF_CPM_PCMCDR_CS(v) (((v) & 0x1) << 30)
+#define BFM_CPM_PCMCDR_CS(v) BM_CPM_PCMCDR_CS
+#define BF_CPM_PCMCDR_CS_V(e) BF_CPM_PCMCDR_CS(BV_CPM_PCMCDR_CS__##e)
+#define BFM_CPM_PCMCDR_CS_V(v) BM_CPM_PCMCDR_CS
+#define BP_CPM_PCMCDR_CE 29
+#define BM_CPM_PCMCDR_CE 0x20000000
+#define BF_CPM_PCMCDR_CE(v) (((v) & 0x1) << 29)
+#define BFM_CPM_PCMCDR_CE(v) BM_CPM_PCMCDR_CE
+#define BF_CPM_PCMCDR_CE_V(e) BF_CPM_PCMCDR_CE(BV_CPM_PCMCDR_CE__##e)
+#define BFM_CPM_PCMCDR_CE_V(v) BM_CPM_PCMCDR_CE
+
+#define REG_CPM_PCMCDR1 jz_reg(CPM_PCMCDR1)
+#define JA_CPM_PCMCDR1 (0xb0000000 + 0xe0)
+#define JT_CPM_PCMCDR1 JIO_32_RW
+#define JN_CPM_PCMCDR1 CPM_PCMCDR1
+#define JI_CPM_PCMCDR1
+#define BP_CPM_PCMCDR1_DIV_D 0
+#define BM_CPM_PCMCDR1_DIV_D 0x1fff
+#define BF_CPM_PCMCDR1_DIV_D(v) (((v) & 0x1fff) << 0)
+#define BFM_CPM_PCMCDR1_DIV_D(v) BM_CPM_PCMCDR1_DIV_D
+#define BF_CPM_PCMCDR1_DIV_D_V(e) BF_CPM_PCMCDR1_DIV_D(BV_CPM_PCMCDR1_DIV_D__##e)
+#define BFM_CPM_PCMCDR1_DIV_D_V(v) BM_CPM_PCMCDR1_DIV_D
+#define BP_CPM_PCMCDR1_N_EN 31
+#define BM_CPM_PCMCDR1_N_EN 0x80000000
+#define BF_CPM_PCMCDR1_N_EN(v) (((v) & 0x1) << 31)
+#define BFM_CPM_PCMCDR1_N_EN(v) BM_CPM_PCMCDR1_N_EN
+#define BF_CPM_PCMCDR1_N_EN_V(e) BF_CPM_PCMCDR1_N_EN(BV_CPM_PCMCDR1_N_EN__##e)
+#define BFM_CPM_PCMCDR1_N_EN_V(v) BM_CPM_PCMCDR1_N_EN
+#define BP_CPM_PCMCDR1_D_EN 30
+#define BM_CPM_PCMCDR1_D_EN 0x40000000
+#define BF_CPM_PCMCDR1_D_EN(v) (((v) & 0x1) << 30)
+#define BFM_CPM_PCMCDR1_D_EN(v) BM_CPM_PCMCDR1_D_EN
+#define BF_CPM_PCMCDR1_D_EN_V(e) BF_CPM_PCMCDR1_D_EN(BV_CPM_PCMCDR1_D_EN__##e)
+#define BFM_CPM_PCMCDR1_D_EN_V(v) BM_CPM_PCMCDR1_D_EN
+
+#define REG_CPM_INTR jz_reg(CPM_INTR)
+#define JA_CPM_INTR (0xb0000000 + 0xb0)
+#define JT_CPM_INTR JIO_32_RW
+#define JN_CPM_INTR CPM_INTR
+#define JI_CPM_INTR
+#define BP_CPM_INTR_VBUS 1
+#define BM_CPM_INTR_VBUS 0x2
+#define BF_CPM_INTR_VBUS(v) (((v) & 0x1) << 1)
+#define BFM_CPM_INTR_VBUS(v) BM_CPM_INTR_VBUS
+#define BF_CPM_INTR_VBUS_V(e) BF_CPM_INTR_VBUS(BV_CPM_INTR_VBUS__##e)
+#define BFM_CPM_INTR_VBUS_V(v) BM_CPM_INTR_VBUS
+#define BP_CPM_INTR_ADEV 0
+#define BM_CPM_INTR_ADEV 0x1
+#define BF_CPM_INTR_ADEV(v) (((v) & 0x1) << 0)
+#define BFM_CPM_INTR_ADEV(v) BM_CPM_INTR_ADEV
+#define BF_CPM_INTR_ADEV_V(e) BF_CPM_INTR_ADEV(BV_CPM_INTR_ADEV__##e)
+#define BFM_CPM_INTR_ADEV_V(v) BM_CPM_INTR_ADEV
+
+#define REG_CPM_INTR_EN jz_reg(CPM_INTR_EN)
+#define JA_CPM_INTR_EN (0xb0000000 + 0xb4)
+#define JT_CPM_INTR_EN JIO_32_RW
+#define JN_CPM_INTR_EN CPM_INTR_EN
+#define JI_CPM_INTR_EN
+#define BP_CPM_INTR_EN_VBUS 1
+#define BM_CPM_INTR_EN_VBUS 0x2
+#define BF_CPM_INTR_EN_VBUS(v) (((v) & 0x1) << 1)
+#define BFM_CPM_INTR_EN_VBUS(v) BM_CPM_INTR_EN_VBUS
+#define BF_CPM_INTR_EN_VBUS_V(e) BF_CPM_INTR_EN_VBUS(BV_CPM_INTR_EN_VBUS__##e)
+#define BFM_CPM_INTR_EN_VBUS_V(v) BM_CPM_INTR_EN_VBUS
+#define BP_CPM_INTR_EN_ADEV 0
+#define BM_CPM_INTR_EN_ADEV 0x1
+#define BF_CPM_INTR_EN_ADEV(v) (((v) & 0x1) << 0)
+#define BFM_CPM_INTR_EN_ADEV(v) BM_CPM_INTR_EN_ADEV
+#define BF_CPM_INTR_EN_ADEV_V(e) BF_CPM_INTR_EN_ADEV(BV_CPM_INTR_EN_ADEV__##e)
+#define BFM_CPM_INTR_EN_ADEV_V(v) BM_CPM_INTR_EN_ADEV
+
#define REG_CPM_DRCG jz_reg(CPM_DRCG)
#define JA_CPM_DRCG (0xb0000000 + 0xd0)
#define JT_CPM_DRCG JIO_32_RW
#define JN_CPM_DRCG CPM_DRCG
#define JI_CPM_DRCG
+#define REG_CPM_SCRATCH_PROT jz_reg(CPM_SCRATCH_PROT)
+#define JA_CPM_SCRATCH_PROT (0xb0000000 + 0x38)
+#define JT_CPM_SCRATCH_PROT JIO_32_RW
+#define JN_CPM_SCRATCH_PROT CPM_SCRATCH_PROT
+#define JI_CPM_SCRATCH_PROT
+
+#define REG_CPM_SCRATCH jz_reg(CPM_SCRATCH)
+#define JA_CPM_SCRATCH (0xb0000000 + 0x34)
+#define JT_CPM_SCRATCH JIO_32_RW
+#define JN_CPM_SCRATCH CPM_SCRATCH
+#define JI_CPM_SCRATCH
+
+#define REG_CPM_USBPCR jz_reg(CPM_USBPCR)
+#define JA_CPM_USBPCR (0xb0000000 + 0x3c)
+#define JT_CPM_USBPCR JIO_32_RW
+#define JN_CPM_USBPCR CPM_USBPCR
+#define JI_CPM_USBPCR
+#define BP_CPM_USBPCR_IDPULLUP_MASK 28
+#define BM_CPM_USBPCR_IDPULLUP_MASK 0x30000000
+#define BV_CPM_USBPCR_IDPULLUP_MASK__ALWAYS 0x2
+#define BV_CPM_USBPCR_IDPULLUP_MASK__ALWAYS_SUSPEND 0x1
+#define BV_CPM_USBPCR_IDPULLUP_MASK__FROM_OTG 0x0
+#define BF_CPM_USBPCR_IDPULLUP_MASK(v) (((v) & 0x3) << 28)
+#define BFM_CPM_USBPCR_IDPULLUP_MASK(v) BM_CPM_USBPCR_IDPULLUP_MASK
+#define BF_CPM_USBPCR_IDPULLUP_MASK_V(e) BF_CPM_USBPCR_IDPULLUP_MASK(BV_CPM_USBPCR_IDPULLUP_MASK__##e)
+#define BFM_CPM_USBPCR_IDPULLUP_MASK_V(v) BM_CPM_USBPCR_IDPULLUP_MASK
+#define BP_CPM_USBPCR_COMPDISTUNE 17
+#define BM_CPM_USBPCR_COMPDISTUNE 0xe0000
+#define BF_CPM_USBPCR_COMPDISTUNE(v) (((v) & 0x7) << 17)
+#define BFM_CPM_USBPCR_COMPDISTUNE(v) BM_CPM_USBPCR_COMPDISTUNE
+#define BF_CPM_USBPCR_COMPDISTUNE_V(e) BF_CPM_USBPCR_COMPDISTUNE(BV_CPM_USBPCR_COMPDISTUNE__##e)
+#define BFM_CPM_USBPCR_COMPDISTUNE_V(v) BM_CPM_USBPCR_COMPDISTUNE
+#define BP_CPM_USBPCR_OTGTUNE 14
+#define BM_CPM_USBPCR_OTGTUNE 0x1c000
+#define BF_CPM_USBPCR_OTGTUNE(v) (((v) & 0x7) << 14)
+#define BFM_CPM_USBPCR_OTGTUNE(v) BM_CPM_USBPCR_OTGTUNE
+#define BF_CPM_USBPCR_OTGTUNE_V(e) BF_CPM_USBPCR_OTGTUNE(BV_CPM_USBPCR_OTGTUNE__##e)
+#define BFM_CPM_USBPCR_OTGTUNE_V(v) BM_CPM_USBPCR_OTGTUNE
+#define BP_CPM_USBPCR_SQRXTUNE 11
+#define BM_CPM_USBPCR_SQRXTUNE 0x3800
+#define BF_CPM_USBPCR_SQRXTUNE(v) (((v) & 0x7) << 11)
+#define BFM_CPM_USBPCR_SQRXTUNE(v) BM_CPM_USBPCR_SQRXTUNE
+#define BF_CPM_USBPCR_SQRXTUNE_V(e) BF_CPM_USBPCR_SQRXTUNE(BV_CPM_USBPCR_SQRXTUNE__##e)
+#define BFM_CPM_USBPCR_SQRXTUNE_V(v) BM_CPM_USBPCR_SQRXTUNE
+#define BP_CPM_USBPCR_TXFSLSTUNE 7
+#define BM_CPM_USBPCR_TXFSLSTUNE 0x780
+#define BF_CPM_USBPCR_TXFSLSTUNE(v) (((v) & 0xf) << 7)
+#define BFM_CPM_USBPCR_TXFSLSTUNE(v) BM_CPM_USBPCR_TXFSLSTUNE
+#define BF_CPM_USBPCR_TXFSLSTUNE_V(e) BF_CPM_USBPCR_TXFSLSTUNE(BV_CPM_USBPCR_TXFSLSTUNE__##e)
+#define BFM_CPM_USBPCR_TXFSLSTUNE_V(v) BM_CPM_USBPCR_TXFSLSTUNE
+#define BP_CPM_USBPCR_TXHSXVTUNE 4
+#define BM_CPM_USBPCR_TXHSXVTUNE 0x30
+#define BF_CPM_USBPCR_TXHSXVTUNE(v) (((v) & 0x3) << 4)
+#define BFM_CPM_USBPCR_TXHSXVTUNE(v) BM_CPM_USBPCR_TXHSXVTUNE
+#define BF_CPM_USBPCR_TXHSXVTUNE_V(e) BF_CPM_USBPCR_TXHSXVTUNE(BV_CPM_USBPCR_TXHSXVTUNE__##e)
+#define BFM_CPM_USBPCR_TXHSXVTUNE_V(v) BM_CPM_USBPCR_TXHSXVTUNE
+#define BP_CPM_USBPCR_TXVREFTUNE 0
+#define BM_CPM_USBPCR_TXVREFTUNE 0xf
+#define BF_CPM_USBPCR_TXVREFTUNE(v) (((v) & 0xf) << 0)
+#define BFM_CPM_USBPCR_TXVREFTUNE(v) BM_CPM_USBPCR_TXVREFTUNE
+#define BF_CPM_USBPCR_TXVREFTUNE_V(e) BF_CPM_USBPCR_TXVREFTUNE(BV_CPM_USBPCR_TXVREFTUNE__##e)
+#define BFM_CPM_USBPCR_TXVREFTUNE_V(v) BM_CPM_USBPCR_TXVREFTUNE
+#define BP_CPM_USBPCR_USB_MODE 31
+#define BM_CPM_USBPCR_USB_MODE 0x80000000
+#define BV_CPM_USBPCR_USB_MODE__USB 0x0
+#define BV_CPM_USBPCR_USB_MODE__OTG 0x1
+#define BF_CPM_USBPCR_USB_MODE(v) (((v) & 0x1) << 31)
+#define BFM_CPM_USBPCR_USB_MODE(v) BM_CPM_USBPCR_USB_MODE
+#define BF_CPM_USBPCR_USB_MODE_V(e) BF_CPM_USBPCR_USB_MODE(BV_CPM_USBPCR_USB_MODE__##e)
+#define BFM_CPM_USBPCR_USB_MODE_V(v) BM_CPM_USBPCR_USB_MODE
+#define BP_CPM_USBPCR_AVLD_REG 30
+#define BM_CPM_USBPCR_AVLD_REG 0x40000000
+#define BF_CPM_USBPCR_AVLD_REG(v) (((v) & 0x1) << 30)
+#define BFM_CPM_USBPCR_AVLD_REG(v) BM_CPM_USBPCR_AVLD_REG
+#define BF_CPM_USBPCR_AVLD_REG_V(e) BF_CPM_USBPCR_AVLD_REG(BV_CPM_USBPCR_AVLD_REG__##e)
+#define BFM_CPM_USBPCR_AVLD_REG_V(v) BM_CPM_USBPCR_AVLD_REG
+#define BP_CPM_USBPCR_INCR_MASK 27
+#define BM_CPM_USBPCR_INCR_MASK 0x8000000
+#define BF_CPM_USBPCR_INCR_MASK(v) (((v) & 0x1) << 27)
+#define BFM_CPM_USBPCR_INCR_MASK(v) BM_CPM_USBPCR_INCR_MASK
+#define BF_CPM_USBPCR_INCR_MASK_V(e) BF_CPM_USBPCR_INCR_MASK(BV_CPM_USBPCR_INCR_MASK__##e)
+#define BFM_CPM_USBPCR_INCR_MASK_V(v) BM_CPM_USBPCR_INCR_MASK
+#define BP_CPM_USBPCR_TXRISETUNE 26
+#define BM_CPM_USBPCR_TXRISETUNE 0x4000000
+#define BF_CPM_USBPCR_TXRISETUNE(v) (((v) & 0x1) << 26)
+#define BFM_CPM_USBPCR_TXRISETUNE(v) BM_CPM_USBPCR_TXRISETUNE
+#define BF_CPM_USBPCR_TXRISETUNE_V(e) BF_CPM_USBPCR_TXRISETUNE(BV_CPM_USBPCR_TXRISETUNE__##e)
+#define BFM_CPM_USBPCR_TXRISETUNE_V(v) BM_CPM_USBPCR_TXRISETUNE
+#define BP_CPM_USBPCR_COMMONONN 25
+#define BM_CPM_USBPCR_COMMONONN 0x2000000
+#define BF_CPM_USBPCR_COMMONONN(v) (((v) & 0x1) << 25)
+#define BFM_CPM_USBPCR_COMMONONN(v) BM_CPM_USBPCR_COMMONONN
+#define BF_CPM_USBPCR_COMMONONN_V(e) BF_CPM_USBPCR_COMMONONN(BV_CPM_USBPCR_COMMONONN__##e)
+#define BFM_CPM_USBPCR_COMMONONN_V(v) BM_CPM_USBPCR_COMMONONN
+#define BP_CPM_USBPCR_VBUSVLDEXT 24
+#define BM_CPM_USBPCR_VBUSVLDEXT 0x1000000
+#define BF_CPM_USBPCR_VBUSVLDEXT(v) (((v) & 0x1) << 24)
+#define BFM_CPM_USBPCR_VBUSVLDEXT(v) BM_CPM_USBPCR_VBUSVLDEXT
+#define BF_CPM_USBPCR_VBUSVLDEXT_V(e) BF_CPM_USBPCR_VBUSVLDEXT(BV_CPM_USBPCR_VBUSVLDEXT__##e)
+#define BFM_CPM_USBPCR_VBUSVLDEXT_V(v) BM_CPM_USBPCR_VBUSVLDEXT
+#define BP_CPM_USBPCR_VBUSVLDEXTSEL 23
+#define BM_CPM_USBPCR_VBUSVLDEXTSEL 0x800000
+#define BF_CPM_USBPCR_VBUSVLDEXTSEL(v) (((v) & 0x1) << 23)
+#define BFM_CPM_USBPCR_VBUSVLDEXTSEL(v) BM_CPM_USBPCR_VBUSVLDEXTSEL
+#define BF_CPM_USBPCR_VBUSVLDEXTSEL_V(e) BF_CPM_USBPCR_VBUSVLDEXTSEL(BV_CPM_USBPCR_VBUSVLDEXTSEL__##e)
+#define BFM_CPM_USBPCR_VBUSVLDEXTSEL_V(v) BM_CPM_USBPCR_VBUSVLDEXTSEL
+#define BP_CPM_USBPCR_POR 22
+#define BM_CPM_USBPCR_POR 0x400000
+#define BF_CPM_USBPCR_POR(v) (((v) & 0x1) << 22)
+#define BFM_CPM_USBPCR_POR(v) BM_CPM_USBPCR_POR
+#define BF_CPM_USBPCR_POR_V(e) BF_CPM_USBPCR_POR(BV_CPM_USBPCR_POR__##e)
+#define BFM_CPM_USBPCR_POR_V(v) BM_CPM_USBPCR_POR
+#define BP_CPM_USBPCR_SIDDQ 21
+#define BM_CPM_USBPCR_SIDDQ 0x200000
+#define BF_CPM_USBPCR_SIDDQ(v) (((v) & 0x1) << 21)
+#define BFM_CPM_USBPCR_SIDDQ(v) BM_CPM_USBPCR_SIDDQ
+#define BF_CPM_USBPCR_SIDDQ_V(e) BF_CPM_USBPCR_SIDDQ(BV_CPM_USBPCR_SIDDQ__##e)
+#define BFM_CPM_USBPCR_SIDDQ_V(v) BM_CPM_USBPCR_SIDDQ
+#define BP_CPM_USBPCR_OTG_DISABLE 20
+#define BM_CPM_USBPCR_OTG_DISABLE 0x100000
+#define BF_CPM_USBPCR_OTG_DISABLE(v) (((v) & 0x1) << 20)
+#define BFM_CPM_USBPCR_OTG_DISABLE(v) BM_CPM_USBPCR_OTG_DISABLE
+#define BF_CPM_USBPCR_OTG_DISABLE_V(e) BF_CPM_USBPCR_OTG_DISABLE(BV_CPM_USBPCR_OTG_DISABLE__##e)
+#define BFM_CPM_USBPCR_OTG_DISABLE_V(v) BM_CPM_USBPCR_OTG_DISABLE
+#define BP_CPM_USBPCR_TXPREEMPHTUNE 6
+#define BM_CPM_USBPCR_TXPREEMPHTUNE 0x40
+#define BF_CPM_USBPCR_TXPREEMPHTUNE(v) (((v) & 0x1) << 6)
+#define BFM_CPM_USBPCR_TXPREEMPHTUNE(v) BM_CPM_USBPCR_TXPREEMPHTUNE
+#define BF_CPM_USBPCR_TXPREEMPHTUNE_V(e) BF_CPM_USBPCR_TXPREEMPHTUNE(BV_CPM_USBPCR_TXPREEMPHTUNE__##e)
+#define BFM_CPM_USBPCR_TXPREEMPHTUNE_V(v) BM_CPM_USBPCR_TXPREEMPHTUNE
+
+#define REG_CPM_USBRDT jz_reg(CPM_USBRDT)
+#define JA_CPM_USBRDT (0xb0000000 + 0x40)
+#define JT_CPM_USBRDT JIO_32_RW
+#define JN_CPM_USBRDT CPM_USBRDT
+#define JI_CPM_USBRDT
+#define BP_CPM_USBRDT_RDT 0
+#define BM_CPM_USBRDT_RDT 0x7fffff
+#define BF_CPM_USBRDT_RDT(v) (((v) & 0x7fffff) << 0)
+#define BFM_CPM_USBRDT_RDT(v) BM_CPM_USBRDT_RDT
+#define BF_CPM_USBRDT_RDT_V(e) BF_CPM_USBRDT_RDT(BV_CPM_USBRDT_RDT__##e)
+#define BFM_CPM_USBRDT_RDT_V(v) BM_CPM_USBRDT_RDT
+#define BP_CPM_USBRDT_HB_MASK 26
+#define BM_CPM_USBRDT_HB_MASK 0x4000000
+#define BF_CPM_USBRDT_HB_MASK(v) (((v) & 0x1) << 26)
+#define BFM_CPM_USBRDT_HB_MASK(v) BM_CPM_USBRDT_HB_MASK
+#define BF_CPM_USBRDT_HB_MASK_V(e) BF_CPM_USBRDT_HB_MASK(BV_CPM_USBRDT_HB_MASK__##e)
+#define BFM_CPM_USBRDT_HB_MASK_V(v) BM_CPM_USBRDT_HB_MASK
+#define BP_CPM_USBRDT_VBFIL_LD_EN 25
+#define BM_CPM_USBRDT_VBFIL_LD_EN 0x2000000
+#define BF_CPM_USBRDT_VBFIL_LD_EN(v) (((v) & 0x1) << 25)
+#define BFM_CPM_USBRDT_VBFIL_LD_EN(v) BM_CPM_USBRDT_VBFIL_LD_EN
+#define BF_CPM_USBRDT_VBFIL_LD_EN_V(e) BF_CPM_USBRDT_VBFIL_LD_EN(BV_CPM_USBRDT_VBFIL_LD_EN__##e)
+#define BFM_CPM_USBRDT_VBFIL_LD_EN_V(v) BM_CPM_USBRDT_VBFIL_LD_EN
+#define BP_CPM_USBRDT_IDDIG_EN 24
+#define BM_CPM_USBRDT_IDDIG_EN 0x1000000
+#define BF_CPM_USBRDT_IDDIG_EN(v) (((v) & 0x1) << 24)
+#define BFM_CPM_USBRDT_IDDIG_EN(v) BM_CPM_USBRDT_IDDIG_EN
+#define BF_CPM_USBRDT_IDDIG_EN_V(e) BF_CPM_USBRDT_IDDIG_EN(BV_CPM_USBRDT_IDDIG_EN__##e)
+#define BFM_CPM_USBRDT_IDDIG_EN_V(v) BM_CPM_USBRDT_IDDIG_EN
+#define BP_CPM_USBRDT_IDDIG_REG 23
+#define BM_CPM_USBRDT_IDDIG_REG 0x800000
+#define BF_CPM_USBRDT_IDDIG_REG(v) (((v) & 0x1) << 23)
+#define BFM_CPM_USBRDT_IDDIG_REG(v) BM_CPM_USBRDT_IDDIG_REG
+#define BF_CPM_USBRDT_IDDIG_REG_V(e) BF_CPM_USBRDT_IDDIG_REG(BV_CPM_USBRDT_IDDIG_REG__##e)
+#define BFM_CPM_USBRDT_IDDIG_REG_V(v) BM_CPM_USBRDT_IDDIG_REG
+
+#define REG_CPM_USBVBFIL jz_reg(CPM_USBVBFIL)
+#define JA_CPM_USBVBFIL (0xb0000000 + 0x44)
+#define JT_CPM_USBVBFIL JIO_32_RW
+#define JN_CPM_USBVBFIL CPM_USBVBFIL
+#define JI_CPM_USBVBFIL
+#define BP_CPM_USBVBFIL_IDDIGFIL 16
+#define BM_CPM_USBVBFIL_IDDIGFIL 0xffff0000
+#define BF_CPM_USBVBFIL_IDDIGFIL(v) (((v) & 0xffff) << 16)
+#define BFM_CPM_USBVBFIL_IDDIGFIL(v) BM_CPM_USBVBFIL_IDDIGFIL
+#define BF_CPM_USBVBFIL_IDDIGFIL_V(e) BF_CPM_USBVBFIL_IDDIGFIL(BV_CPM_USBVBFIL_IDDIGFIL__##e)
+#define BFM_CPM_USBVBFIL_IDDIGFIL_V(v) BM_CPM_USBVBFIL_IDDIGFIL
+#define BP_CPM_USBVBFIL_VBFIL 0
+#define BM_CPM_USBVBFIL_VBFIL 0xffff
+#define BF_CPM_USBVBFIL_VBFIL(v) (((v) & 0xffff) << 0)
+#define BFM_CPM_USBVBFIL_VBFIL(v) BM_CPM_USBVBFIL_VBFIL
+#define BF_CPM_USBVBFIL_VBFIL_V(e) BF_CPM_USBVBFIL_VBFIL(BV_CPM_USBVBFIL_VBFIL__##e)
+#define BFM_CPM_USBVBFIL_VBFIL_V(v) BM_CPM_USBVBFIL_VBFIL
+
+#define REG_CPM_USBPCR1 jz_reg(CPM_USBPCR1)
+#define JA_CPM_USBPCR1 (0xb0000000 + 0x48)
+#define JT_CPM_USBPCR1 JIO_32_RW
+#define JN_CPM_USBPCR1 CPM_USBPCR1
+#define JI_CPM_USBPCR1
+#define BP_CPM_USBPCR1_REFCLK_SEL 26
+#define BM_CPM_USBPCR1_REFCLK_SEL 0xc000000
+#define BV_CPM_USBPCR1_REFCLK_SEL__CLKCORE 0x2
+#define BV_CPM_USBPCR1_REFCLK_SEL__EXTERNAL 0x1
+#define BV_CPM_USBPCR1_REFCLK_SEL__CRYSTAL 0x0
+#define BF_CPM_USBPCR1_REFCLK_SEL(v) (((v) & 0x3) << 26)
+#define BFM_CPM_USBPCR1_REFCLK_SEL(v) BM_CPM_USBPCR1_REFCLK_SEL
+#define BF_CPM_USBPCR1_REFCLK_SEL_V(e) BF_CPM_USBPCR1_REFCLK_SEL(BV_CPM_USBPCR1_REFCLK_SEL__##e)
+#define BFM_CPM_USBPCR1_REFCLK_SEL_V(v) BM_CPM_USBPCR1_REFCLK_SEL
+#define BP_CPM_USBPCR1_REFCLK_DIV 24
+#define BM_CPM_USBPCR1_REFCLK_DIV 0x3000000
+#define BV_CPM_USBPCR1_REFCLK_DIV__48MHZ 0x2
+#define BV_CPM_USBPCR1_REFCLK_DIV__24MHZ 0x1
+#define BV_CPM_USBPCR1_REFCLK_DIV__12MHZ 0x0
+#define BF_CPM_USBPCR1_REFCLK_DIV(v) (((v) & 0x3) << 24)
+#define BFM_CPM_USBPCR1_REFCLK_DIV(v) BM_CPM_USBPCR1_REFCLK_DIV
+#define BF_CPM_USBPCR1_REFCLK_DIV_V(e) BF_CPM_USBPCR1_REFCLK_DIV(BV_CPM_USBPCR1_REFCLK_DIV__##e)
+#define BFM_CPM_USBPCR1_REFCLK_DIV_V(v) BM_CPM_USBPCR1_REFCLK_DIV
+#define BP_CPM_USBPCR1_BVLD_REG 31
+#define BM_CPM_USBPCR1_BVLD_REG 0x80000000
+#define BF_CPM_USBPCR1_BVLD_REG(v) (((v) & 0x1) << 31)
+#define BFM_CPM_USBPCR1_BVLD_REG(v) BM_CPM_USBPCR1_BVLD_REG
+#define BF_CPM_USBPCR1_BVLD_REG_V(e) BF_CPM_USBPCR1_BVLD_REG(BV_CPM_USBPCR1_BVLD_REG__##e)
+#define BFM_CPM_USBPCR1_BVLD_REG_V(v) BM_CPM_USBPCR1_BVLD_REG
+#define BP_CPM_USBPCR1_PORT_RST 21
+#define BM_CPM_USBPCR1_PORT_RST 0x200000
+#define BF_CPM_USBPCR1_PORT_RST(v) (((v) & 0x1) << 21)
+#define BFM_CPM_USBPCR1_PORT_RST(v) BM_CPM_USBPCR1_PORT_RST
+#define BF_CPM_USBPCR1_PORT_RST_V(e) BF_CPM_USBPCR1_PORT_RST(BV_CPM_USBPCR1_PORT_RST__##e)
+#define BFM_CPM_USBPCR1_PORT_RST_V(v) BM_CPM_USBPCR1_PORT_RST
+#define BP_CPM_USBPCR1_WORD_IF 19
+#define BM_CPM_USBPCR1_WORD_IF 0x80000
+#define BV_CPM_USBPCR1_WORD_IF__16BIT 0x1
+#define BV_CPM_USBPCR1_WORD_IF__8BIT 0x0
+#define BF_CPM_USBPCR1_WORD_IF(v) (((v) & 0x1) << 19)
+#define BFM_CPM_USBPCR1_WORD_IF(v) BM_CPM_USBPCR1_WORD_IF
+#define BF_CPM_USBPCR1_WORD_IF_V(e) BF_CPM_USBPCR1_WORD_IF(BV_CPM_USBPCR1_WORD_IF__##e)
+#define BFM_CPM_USBPCR1_WORD_IF_V(v) BM_CPM_USBPCR1_WORD_IF
+
#define REG_CPM_APCR jz_reg(CPM_APCR)
#define JA_CPM_APCR (0xb0000000 + 0x10)
#define JT_CPM_APCR JIO_32_RW
@@ -791,6 +1241,102 @@
#define BF_CPM_CLKGR_EFUSE_V(e) BF_CPM_CLKGR_EFUSE(BV_CPM_CLKGR_EFUSE__##e)
#define BFM_CPM_CLKGR_EFUSE_V(v) BM_CPM_CLKGR_EFUSE
+#define REG_CPM_SRBC jz_reg(CPM_SRBC)
+#define JA_CPM_SRBC (0xb0000000 + 0xc4)
+#define JT_CPM_SRBC JIO_32_RW
+#define JN_CPM_SRBC CPM_SRBC
+#define JI_CPM_SRBC
+#define BP_CPM_SRBC_JPEG_SR 31
+#define BM_CPM_SRBC_JPEG_SR 0x80000000
+#define BF_CPM_SRBC_JPEG_SR(v) (((v) & 0x1) << 31)
+#define BFM_CPM_SRBC_JPEG_SR(v) BM_CPM_SRBC_JPEG_SR
+#define BF_CPM_SRBC_JPEG_SR_V(e) BF_CPM_SRBC_JPEG_SR(BV_CPM_SRBC_JPEG_SR__##e)
+#define BFM_CPM_SRBC_JPEG_SR_V(v) BM_CPM_SRBC_JPEG_SR
+#define BP_CPM_SRBC_JPEG_STOP 30
+#define BM_CPM_SRBC_JPEG_STOP 0x40000000
+#define BF_CPM_SRBC_JPEG_STOP(v) (((v) & 0x1) << 30)
+#define BFM_CPM_SRBC_JPEG_STOP(v) BM_CPM_SRBC_JPEG_STOP
+#define BF_CPM_SRBC_JPEG_STOP_V(e) BF_CPM_SRBC_JPEG_STOP(BV_CPM_SRBC_JPEG_STOP__##e)
+#define BFM_CPM_SRBC_JPEG_STOP_V(v) BM_CPM_SRBC_JPEG_STOP
+#define BP_CPM_SRBC_JPEG_ACK 29
+#define BM_CPM_SRBC_JPEG_ACK 0x20000000
+#define BF_CPM_SRBC_JPEG_ACK(v) (((v) & 0x1) << 29)
+#define BFM_CPM_SRBC_JPEG_ACK(v) BM_CPM_SRBC_JPEG_ACK
+#define BF_CPM_SRBC_JPEG_ACK_V(e) BF_CPM_SRBC_JPEG_ACK(BV_CPM_SRBC_JPEG_ACK__##e)
+#define BFM_CPM_SRBC_JPEG_ACK_V(v) BM_CPM_SRBC_JPEG_ACK
+#define BP_CPM_SRBC_LCD_SR 25
+#define BM_CPM_SRBC_LCD_SR 0x2000000
+#define BF_CPM_SRBC_LCD_SR(v) (((v) & 0x1) << 25)
+#define BFM_CPM_SRBC_LCD_SR(v) BM_CPM_SRBC_LCD_SR
+#define BF_CPM_SRBC_LCD_SR_V(e) BF_CPM_SRBC_LCD_SR(BV_CPM_SRBC_LCD_SR__##e)
+#define BFM_CPM_SRBC_LCD_SR_V(v) BM_CPM_SRBC_LCD_SR
+#define BP_CPM_SRBC_LCD_STOP 24
+#define BM_CPM_SRBC_LCD_STOP 0x1000000
+#define BF_CPM_SRBC_LCD_STOP(v) (((v) & 0x1) << 24)
+#define BFM_CPM_SRBC_LCD_STOP(v) BM_CPM_SRBC_LCD_STOP
+#define BF_CPM_SRBC_LCD_STOP_V(e) BF_CPM_SRBC_LCD_STOP(BV_CPM_SRBC_LCD_STOP__##e)
+#define BFM_CPM_SRBC_LCD_STOP_V(v) BM_CPM_SRBC_LCD_STOP
+#define BP_CPM_SRBC_LCD_ACK 23
+#define BM_CPM_SRBC_LCD_ACK 0x800000
+#define BF_CPM_SRBC_LCD_ACK(v) (((v) & 0x1) << 23)
+#define BFM_CPM_SRBC_LCD_ACK(v) BM_CPM_SRBC_LCD_ACK
+#define BF_CPM_SRBC_LCD_ACK_V(e) BF_CPM_SRBC_LCD_ACK(BV_CPM_SRBC_LCD_ACK__##e)
+#define BFM_CPM_SRBC_LCD_ACK_V(v) BM_CPM_SRBC_LCD_ACK
+#define BP_CPM_SRBC_CIM_STOP 21
+#define BM_CPM_SRBC_CIM_STOP 0x200000
+#define BF_CPM_SRBC_CIM_STOP(v) (((v) & 0x1) << 21)
+#define BFM_CPM_SRBC_CIM_STOP(v) BM_CPM_SRBC_CIM_STOP
+#define BF_CPM_SRBC_CIM_STOP_V(e) BF_CPM_SRBC_CIM_STOP(BV_CPM_SRBC_CIM_STOP__##e)
+#define BFM_CPM_SRBC_CIM_STOP_V(v) BM_CPM_SRBC_CIM_STOP
+#define BP_CPM_SRBC_CIM_ACK 20
+#define BM_CPM_SRBC_CIM_ACK 0x100000
+#define BF_CPM_SRBC_CIM_ACK(v) (((v) & 0x1) << 20)
+#define BFM_CPM_SRBC_CIM_ACK(v) BM_CPM_SRBC_CIM_ACK
+#define BF_CPM_SRBC_CIM_ACK_V(e) BF_CPM_SRBC_CIM_ACK(BV_CPM_SRBC_CIM_ACK__##e)
+#define BFM_CPM_SRBC_CIM_ACK_V(v) BM_CPM_SRBC_CIM_ACK
+#define BP_CPM_SRBC_CPU_STOP 15
+#define BM_CPM_SRBC_CPU_STOP 0x8000
+#define BF_CPM_SRBC_CPU_STOP(v) (((v) & 0x1) << 15)
+#define BFM_CPM_SRBC_CPU_STOP(v) BM_CPM_SRBC_CPU_STOP
+#define BF_CPM_SRBC_CPU_STOP_V(e) BF_CPM_SRBC_CPU_STOP(BV_CPM_SRBC_CPU_STOP__##e)
+#define BFM_CPM_SRBC_CPU_STOP_V(v) BM_CPM_SRBC_CPU_STOP
+#define BP_CPM_SRBC_CPU_ACK 14
+#define BM_CPM_SRBC_CPU_ACK 0x4000
+#define BF_CPM_SRBC_CPU_ACK(v) (((v) & 0x1) << 14)
+#define BFM_CPM_SRBC_CPU_ACK(v) BM_CPM_SRBC_CPU_ACK
+#define BF_CPM_SRBC_CPU_ACK_V(e) BF_CPM_SRBC_CPU_ACK(BV_CPM_SRBC_CPU_ACK__##e)
+#define BFM_CPM_SRBC_CPU_ACK_V(v) BM_CPM_SRBC_CPU_ACK
+#define BP_CPM_SRBC_OTG_SR 12
+#define BM_CPM_SRBC_OTG_SR 0x1000
+#define BF_CPM_SRBC_OTG_SR(v) (((v) & 0x1) << 12)
+#define BFM_CPM_SRBC_OTG_SR(v) BM_CPM_SRBC_OTG_SR
+#define BF_CPM_SRBC_OTG_SR_V(e) BF_CPM_SRBC_OTG_SR(BV_CPM_SRBC_OTG_SR__##e)
+#define BFM_CPM_SRBC_OTG_SR_V(v) BM_CPM_SRBC_OTG_SR
+#define BP_CPM_SRBC_AHB2_STOP 8
+#define BM_CPM_SRBC_AHB2_STOP 0x100
+#define BF_CPM_SRBC_AHB2_STOP(v) (((v) & 0x1) << 8)
+#define BFM_CPM_SRBC_AHB2_STOP(v) BM_CPM_SRBC_AHB2_STOP
+#define BF_CPM_SRBC_AHB2_STOP_V(e) BF_CPM_SRBC_AHB2_STOP(BV_CPM_SRBC_AHB2_STOP__##e)
+#define BFM_CPM_SRBC_AHB2_STOP_V(v) BM_CPM_SRBC_AHB2_STOP
+#define BP_CPM_SRBC_AHB2_ACK 7
+#define BM_CPM_SRBC_AHB2_ACK 0x80
+#define BF_CPM_SRBC_AHB2_ACK(v) (((v) & 0x1) << 7)
+#define BFM_CPM_SRBC_AHB2_ACK(v) BM_CPM_SRBC_AHB2_ACK
+#define BF_CPM_SRBC_AHB2_ACK_V(e) BF_CPM_SRBC_AHB2_ACK(BV_CPM_SRBC_AHB2_ACK__##e)
+#define BFM_CPM_SRBC_AHB2_ACK_V(v) BM_CPM_SRBC_AHB2_ACK
+#define BP_CPM_SRBC_DDR_STOP 6
+#define BM_CPM_SRBC_DDR_STOP 0x40
+#define BF_CPM_SRBC_DDR_STOP(v) (((v) & 0x1) << 6)
+#define BFM_CPM_SRBC_DDR_STOP(v) BM_CPM_SRBC_DDR_STOP
+#define BF_CPM_SRBC_DDR_STOP_V(e) BF_CPM_SRBC_DDR_STOP(BV_CPM_SRBC_DDR_STOP__##e)
+#define BFM_CPM_SRBC_DDR_STOP_V(v) BM_CPM_SRBC_DDR_STOP
+#define BP_CPM_SRBC_DDR_ACK 5
+#define BM_CPM_SRBC_DDR_ACK 0x20
+#define BF_CPM_SRBC_DDR_ACK(v) (((v) & 0x1) << 5)
+#define BFM_CPM_SRBC_DDR_ACK(v) BM_CPM_SRBC_DDR_ACK
+#define BF_CPM_SRBC_DDR_ACK_V(e) BF_CPM_SRBC_DDR_ACK(BV_CPM_SRBC_DDR_ACK__##e)
+#define BFM_CPM_SRBC_DDR_ACK_V(v) BM_CPM_SRBC_DDR_ACK
+
#define REG_CPM_OPCR jz_reg(CPM_OPCR)
#define JA_CPM_OPCR (0xb0000000 + 0x24)
#define JT_CPM_OPCR JIO_32_RW
@@ -893,4 +1439,34 @@
#define BF_CPM_OPCR_BUS_MODE_V(e) BF_CPM_OPCR_BUS_MODE(BV_CPM_OPCR_BUS_MODE__##e)
#define BFM_CPM_OPCR_BUS_MODE_V(v) BM_CPM_OPCR_BUS_MODE
+#define REG_CPM_RSR jz_reg(CPM_RSR)
+#define JA_CPM_RSR (0xb0000000 + 0x8)
+#define JT_CPM_RSR JIO_32_RW
+#define JN_CPM_RSR CPM_RSR
+#define JI_CPM_RSR
+#define BP_CPM_RSR_HR 3
+#define BM_CPM_RSR_HR 0x8
+#define BF_CPM_RSR_HR(v) (((v) & 0x1) << 3)
+#define BFM_CPM_RSR_HR(v) BM_CPM_RSR_HR
+#define BF_CPM_RSR_HR_V(e) BF_CPM_RSR_HR(BV_CPM_RSR_HR__##e)
+#define BFM_CPM_RSR_HR_V(v) BM_CPM_RSR_HR
+#define BP_CPM_RSR_P0R 2
+#define BM_CPM_RSR_P0R 0x4
+#define BF_CPM_RSR_P0R(v) (((v) & 0x1) << 2)
+#define BFM_CPM_RSR_P0R(v) BM_CPM_RSR_P0R
+#define BF_CPM_RSR_P0R_V(e) BF_CPM_RSR_P0R(BV_CPM_RSR_P0R__##e)
+#define BFM_CPM_RSR_P0R_V(v) BM_CPM_RSR_P0R
+#define BP_CPM_RSR_WR 1
+#define BM_CPM_RSR_WR 0x2
+#define BF_CPM_RSR_WR(v) (((v) & 0x1) << 1)
+#define BFM_CPM_RSR_WR(v) BM_CPM_RSR_WR
+#define BF_CPM_RSR_WR_V(e) BF_CPM_RSR_WR(BV_CPM_RSR_WR__##e)
+#define BFM_CPM_RSR_WR_V(v) BM_CPM_RSR_WR
+#define BP_CPM_RSR_PR 0
+#define BM_CPM_RSR_PR 0x1
+#define BF_CPM_RSR_PR(v) (((v) & 0x1) << 0)
+#define BFM_CPM_RSR_PR(v) BM_CPM_RSR_PR
+#define BF_CPM_RSR_PR_V(e) BF_CPM_RSR_PR(BV_CPM_RSR_PR__##e)
+#define BFM_CPM_RSR_PR_V(v) BM_CPM_RSR_PR
+
#endif /* __HEADERGEN_CPM_H__*/
diff --git a/firmware/target/mips/ingenic_x1000/x1000/efuse.h b/firmware/target/mips/ingenic_x1000/x1000/efuse.h
new file mode 100644
index 0000000000..8628cfd08b
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/x1000/efuse.h
@@ -0,0 +1,173 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * This file was automatically generated by headergen, DO NOT EDIT it.
+ * headergen version: 3.0.0
+ * x1000 version: 1.0
+ * x1000 authors: Aidan MacDonald
+ *
+ * Copyright (C) 2015 by the authors
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef __HEADERGEN_EFUSE_H__
+#define __HEADERGEN_EFUSE_H__
+
+#include "macro.h"
+
+#define REG_EFUSE_CTRL jz_reg(EFUSE_CTRL)
+#define JA_EFUSE_CTRL (0xb3540000 + 0x0)
+#define JT_EFUSE_CTRL JIO_32_RW
+#define JN_EFUSE_CTRL EFUSE_CTRL
+#define JI_EFUSE_CTRL
+#define BP_EFUSE_CTRL_ADDR 21
+#define BM_EFUSE_CTRL_ADDR 0xfe00000
+#define BF_EFUSE_CTRL_ADDR(v) (((v) & 0x7f) << 21)
+#define BFM_EFUSE_CTRL_ADDR(v) BM_EFUSE_CTRL_ADDR
+#define BF_EFUSE_CTRL_ADDR_V(e) BF_EFUSE_CTRL_ADDR(BV_EFUSE_CTRL_ADDR__##e)
+#define BFM_EFUSE_CTRL_ADDR_V(v) BM_EFUSE_CTRL_ADDR
+#define BP_EFUSE_CTRL_LENGTH 16
+#define BM_EFUSE_CTRL_LENGTH 0x1f0000
+#define BF_EFUSE_CTRL_LENGTH(v) (((v) & 0x1f) << 16)
+#define BFM_EFUSE_CTRL_LENGTH(v) BM_EFUSE_CTRL_LENGTH
+#define BF_EFUSE_CTRL_LENGTH_V(e) BF_EFUSE_CTRL_LENGTH(BV_EFUSE_CTRL_LENGTH__##e)
+#define BFM_EFUSE_CTRL_LENGTH_V(v) BM_EFUSE_CTRL_LENGTH
+#define BP_EFUSE_CTRL_PG_EN 15
+#define BM_EFUSE_CTRL_PG_EN 0x8000
+#define BF_EFUSE_CTRL_PG_EN(v) (((v) & 0x1) << 15)
+#define BFM_EFUSE_CTRL_PG_EN(v) BM_EFUSE_CTRL_PG_EN
+#define BF_EFUSE_CTRL_PG_EN_V(e) BF_EFUSE_CTRL_PG_EN(BV_EFUSE_CTRL_PG_EN__##e)
+#define BFM_EFUSE_CTRL_PG_EN_V(v) BM_EFUSE_CTRL_PG_EN
+#define BP_EFUSE_CTRL_WR_EN 1
+#define BM_EFUSE_CTRL_WR_EN 0x2
+#define BF_EFUSE_CTRL_WR_EN(v) (((v) & 0x1) << 1)
+#define BFM_EFUSE_CTRL_WR_EN(v) BM_EFUSE_CTRL_WR_EN
+#define BF_EFUSE_CTRL_WR_EN_V(e) BF_EFUSE_CTRL_WR_EN(BV_EFUSE_CTRL_WR_EN__##e)
+#define BFM_EFUSE_CTRL_WR_EN_V(v) BM_EFUSE_CTRL_WR_EN
+#define BP_EFUSE_CTRL_RD_EN 0
+#define BM_EFUSE_CTRL_RD_EN 0x1
+#define BF_EFUSE_CTRL_RD_EN(v) (((v) & 0x1) << 0)
+#define BFM_EFUSE_CTRL_RD_EN(v) BM_EFUSE_CTRL_RD_EN
+#define BF_EFUSE_CTRL_RD_EN_V(e) BF_EFUSE_CTRL_RD_EN(BV_EFUSE_CTRL_RD_EN__##e)
+#define BFM_EFUSE_CTRL_RD_EN_V(v) BM_EFUSE_CTRL_RD_EN
+
+#define REG_EFUSE_CFG jz_reg(EFUSE_CFG)
+#define JA_EFUSE_CFG (0xb3540000 + 0x4)
+#define JT_EFUSE_CFG JIO_32_RW
+#define JN_EFUSE_CFG EFUSE_CFG
+#define JI_EFUSE_CFG
+#define BP_EFUSE_CFG_RD_AJD 20
+#define BM_EFUSE_CFG_RD_AJD 0x300000
+#define BF_EFUSE_CFG_RD_AJD(v) (((v) & 0x3) << 20)
+#define BFM_EFUSE_CFG_RD_AJD(v) BM_EFUSE_CFG_RD_AJD
+#define BF_EFUSE_CFG_RD_AJD_V(e) BF_EFUSE_CFG_RD_AJD(BV_EFUSE_CFG_RD_AJD__##e)
+#define BFM_EFUSE_CFG_RD_AJD_V(v) BM_EFUSE_CFG_RD_AJD
+#define BP_EFUSE_CFG_RD_STROBE 16
+#define BM_EFUSE_CFG_RD_STROBE 0x70000
+#define BF_EFUSE_CFG_RD_STROBE(v) (((v) & 0x7) << 16)
+#define BFM_EFUSE_CFG_RD_STROBE(v) BM_EFUSE_CFG_RD_STROBE
+#define BF_EFUSE_CFG_RD_STROBE_V(e) BF_EFUSE_CFG_RD_STROBE(BV_EFUSE_CFG_RD_STROBE__##e)
+#define BFM_EFUSE_CFG_RD_STROBE_V(v) BM_EFUSE_CFG_RD_STROBE
+#define BP_EFUSE_CFG_WR_ADJ 12
+#define BM_EFUSE_CFG_WR_ADJ 0x3000
+#define BF_EFUSE_CFG_WR_ADJ(v) (((v) & 0x3) << 12)
+#define BFM_EFUSE_CFG_WR_ADJ(v) BM_EFUSE_CFG_WR_ADJ
+#define BF_EFUSE_CFG_WR_ADJ_V(e) BF_EFUSE_CFG_WR_ADJ(BV_EFUSE_CFG_WR_ADJ__##e)
+#define BFM_EFUSE_CFG_WR_ADJ_V(v) BM_EFUSE_CFG_WR_ADJ
+#define BP_EFUSE_CFG_WR_STROBE 0
+#define BM_EFUSE_CFG_WR_STROBE 0x1ff
+#define BF_EFUSE_CFG_WR_STROBE(v) (((v) & 0x1ff) << 0)
+#define BFM_EFUSE_CFG_WR_STROBE(v) BM_EFUSE_CFG_WR_STROBE
+#define BF_EFUSE_CFG_WR_STROBE_V(e) BF_EFUSE_CFG_WR_STROBE(BV_EFUSE_CFG_WR_STROBE__##e)
+#define BFM_EFUSE_CFG_WR_STROBE_V(v) BM_EFUSE_CFG_WR_STROBE
+#define BP_EFUSE_CFG_INT_EN 31
+#define BM_EFUSE_CFG_INT_EN 0x80000000
+#define BF_EFUSE_CFG_INT_EN(v) (((v) & 0x1) << 31)
+#define BFM_EFUSE_CFG_INT_EN(v) BM_EFUSE_CFG_INT_EN
+#define BF_EFUSE_CFG_INT_EN_V(e) BF_EFUSE_CFG_INT_EN(BV_EFUSE_CFG_INT_EN__##e)
+#define BFM_EFUSE_CFG_INT_EN_V(v) BM_EFUSE_CFG_INT_EN
+
+#define REG_EFUSE_STATE jz_reg(EFUSE_STATE)
+#define JA_EFUSE_STATE (0xb3540000 + 0x8)
+#define JT_EFUSE_STATE JIO_32_RW
+#define JN_EFUSE_STATE EFUSE_STATE
+#define JI_EFUSE_STATE
+#define BP_EFUSE_STATE_UK_PRT 23
+#define BM_EFUSE_STATE_UK_PRT 0x800000
+#define BF_EFUSE_STATE_UK_PRT(v) (((v) & 0x1) << 23)
+#define BFM_EFUSE_STATE_UK_PRT(v) BM_EFUSE_STATE_UK_PRT
+#define BF_EFUSE_STATE_UK_PRT_V(e) BF_EFUSE_STATE_UK_PRT(BV_EFUSE_STATE_UK_PRT__##e)
+#define BFM_EFUSE_STATE_UK_PRT_V(v) BM_EFUSE_STATE_UK_PRT
+#define BP_EFUSE_STATE_NKU_PRT 22
+#define BM_EFUSE_STATE_NKU_PRT 0x400000
+#define BF_EFUSE_STATE_NKU_PRT(v) (((v) & 0x1) << 22)
+#define BFM_EFUSE_STATE_NKU_PRT(v) BM_EFUSE_STATE_NKU_PRT
+#define BF_EFUSE_STATE_NKU_PRT_V(e) BF_EFUSE_STATE_NKU_PRT(BV_EFUSE_STATE_NKU_PRT__##e)
+#define BFM_EFUSE_STATE_NKU_PRT_V(v) BM_EFUSE_STATE_NKU_PRT
+#define BP_EFUSE_STATE_EXKEY_EN 21
+#define BM_EFUSE_STATE_EXKEY_EN 0x200000
+#define BF_EFUSE_STATE_EXKEY_EN(v) (((v) & 0x1) << 21)
+#define BFM_EFUSE_STATE_EXKEY_EN(v) BM_EFUSE_STATE_EXKEY_EN
+#define BF_EFUSE_STATE_EXKEY_EN_V(e) BF_EFUSE_STATE_EXKEY_EN(BV_EFUSE_STATE_EXKEY_EN__##e)
+#define BFM_EFUSE_STATE_EXKEY_EN_V(v) BM_EFUSE_STATE_EXKEY_EN
+#define BP_EFUSE_STATE_CUSTID_PRT 15
+#define BM_EFUSE_STATE_CUSTID_PRT 0x8000
+#define BF_EFUSE_STATE_CUSTID_PRT(v) (((v) & 0x1) << 15)
+#define BFM_EFUSE_STATE_CUSTID_PRT(v) BM_EFUSE_STATE_CUSTID_PRT
+#define BF_EFUSE_STATE_CUSTID_PRT_V(e) BF_EFUSE_STATE_CUSTID_PRT(BV_EFUSE_STATE_CUSTID_PRT__##e)
+#define BFM_EFUSE_STATE_CUSTID_PRT_V(v) BM_EFUSE_STATE_CUSTID_PRT
+#define BP_EFUSE_STATE_CHIPID_PRT 14
+#define BM_EFUSE_STATE_CHIPID_PRT 0x4000
+#define BF_EFUSE_STATE_CHIPID_PRT(v) (((v) & 0x1) << 14)
+#define BFM_EFUSE_STATE_CHIPID_PRT(v) BM_EFUSE_STATE_CHIPID_PRT
+#define BF_EFUSE_STATE_CHIPID_PRT_V(e) BF_EFUSE_STATE_CHIPID_PRT(BV_EFUSE_STATE_CHIPID_PRT__##e)
+#define BFM_EFUSE_STATE_CHIPID_PRT_V(v) BM_EFUSE_STATE_CHIPID_PRT
+#define BP_EFUSE_STATE_SECBOOT_PRT 12
+#define BM_EFUSE_STATE_SECBOOT_PRT 0x1000
+#define BF_EFUSE_STATE_SECBOOT_PRT(v) (((v) & 0x1) << 12)
+#define BFM_EFUSE_STATE_SECBOOT_PRT(v) BM_EFUSE_STATE_SECBOOT_PRT
+#define BF_EFUSE_STATE_SECBOOT_PRT_V(e) BF_EFUSE_STATE_SECBOOT_PRT(BV_EFUSE_STATE_SECBOOT_PRT__##e)
+#define BFM_EFUSE_STATE_SECBOOT_PRT_V(v) BM_EFUSE_STATE_SECBOOT_PRT
+#define BP_EFUSE_STATE_DIS_JTAG 11
+#define BM_EFUSE_STATE_DIS_JTAG 0x800
+#define BF_EFUSE_STATE_DIS_JTAG(v) (((v) & 0x1) << 11)
+#define BFM_EFUSE_STATE_DIS_JTAG(v) BM_EFUSE_STATE_DIS_JTAG
+#define BF_EFUSE_STATE_DIS_JTAG_V(e) BF_EFUSE_STATE_DIS_JTAG(BV_EFUSE_STATE_DIS_JTAG__##e)
+#define BFM_EFUSE_STATE_DIS_JTAG_V(v) BM_EFUSE_STATE_DIS_JTAG
+#define BP_EFUSE_STATE_SECBOOT_EN 8
+#define BM_EFUSE_STATE_SECBOOT_EN 0x100
+#define BF_EFUSE_STATE_SECBOOT_EN(v) (((v) & 0x1) << 8)
+#define BFM_EFUSE_STATE_SECBOOT_EN(v) BM_EFUSE_STATE_SECBOOT_EN
+#define BF_EFUSE_STATE_SECBOOT_EN_V(e) BF_EFUSE_STATE_SECBOOT_EN(BV_EFUSE_STATE_SECBOOT_EN__##e)
+#define BFM_EFUSE_STATE_SECBOOT_EN_V(v) BM_EFUSE_STATE_SECBOOT_EN
+#define BP_EFUSE_STATE_WR_DONE 1
+#define BM_EFUSE_STATE_WR_DONE 0x2
+#define BF_EFUSE_STATE_WR_DONE(v) (((v) & 0x1) << 1)
+#define BFM_EFUSE_STATE_WR_DONE(v) BM_EFUSE_STATE_WR_DONE
+#define BF_EFUSE_STATE_WR_DONE_V(e) BF_EFUSE_STATE_WR_DONE(BV_EFUSE_STATE_WR_DONE__##e)
+#define BFM_EFUSE_STATE_WR_DONE_V(v) BM_EFUSE_STATE_WR_DONE
+#define BP_EFUSE_STATE_RD_DONE 0
+#define BM_EFUSE_STATE_RD_DONE 0x1
+#define BF_EFUSE_STATE_RD_DONE(v) (((v) & 0x1) << 0)
+#define BFM_EFUSE_STATE_RD_DONE(v) BM_EFUSE_STATE_RD_DONE
+#define BF_EFUSE_STATE_RD_DONE_V(e) BF_EFUSE_STATE_RD_DONE(BV_EFUSE_STATE_RD_DONE__##e)
+#define BFM_EFUSE_STATE_RD_DONE_V(v) BM_EFUSE_STATE_RD_DONE
+
+#define REG_EFUSE_DATA(_n1) jz_reg(EFUSE_DATA(_n1))
+#define JA_EFUSE_DATA(_n1) (0xb3540000 + 0xc + (_n1) * 0x4)
+#define JT_EFUSE_DATA(_n1) JIO_32_RW
+#define JN_EFUSE_DATA(_n1) EFUSE_DATA
+#define JI_EFUSE_DATA(_n1) (_n1)
+
+#endif /* __HEADERGEN_EFUSE_H__*/
diff --git a/firmware/target/mips/ingenic_x1000/x1000/ost.h b/firmware/target/mips/ingenic_x1000/x1000/ost.h
index 8f2619e0e7..9444712e86 100644
--- a/firmware/target/mips/ingenic_x1000/x1000/ost.h
+++ b/firmware/target/mips/ingenic_x1000/x1000/ost.h
@@ -31,21 +31,21 @@
#define JT_OST_CTRL JIO_32_RW
#define JN_OST_CTRL OST_CTRL
#define JI_OST_CTRL
-#define BP_OST_CTRL_PRESCALE2 3
-#define BM_OST_CTRL_PRESCALE2 0x38
+#define BP_OST_CTRL_PRESCALE2 2
+#define BM_OST_CTRL_PRESCALE2 0xc
#define BV_OST_CTRL_PRESCALE2__BY_1 0x0
#define BV_OST_CTRL_PRESCALE2__BY_4 0x1
#define BV_OST_CTRL_PRESCALE2__BY_16 0x2
-#define BF_OST_CTRL_PRESCALE2(v) (((v) & 0x7) << 3)
+#define BF_OST_CTRL_PRESCALE2(v) (((v) & 0x3) << 2)
#define BFM_OST_CTRL_PRESCALE2(v) BM_OST_CTRL_PRESCALE2
#define BF_OST_CTRL_PRESCALE2_V(e) BF_OST_CTRL_PRESCALE2(BV_OST_CTRL_PRESCALE2__##e)
#define BFM_OST_CTRL_PRESCALE2_V(v) BM_OST_CTRL_PRESCALE2
#define BP_OST_CTRL_PRESCALE1 0
-#define BM_OST_CTRL_PRESCALE1 0x7
+#define BM_OST_CTRL_PRESCALE1 0x3
#define BV_OST_CTRL_PRESCALE1__BY_1 0x0
#define BV_OST_CTRL_PRESCALE1__BY_4 0x1
#define BV_OST_CTRL_PRESCALE1__BY_16 0x2
-#define BF_OST_CTRL_PRESCALE1(v) (((v) & 0x7) << 0)
+#define BF_OST_CTRL_PRESCALE1(v) (((v) & 0x3) << 0)
#define BFM_OST_CTRL_PRESCALE1(v) BM_OST_CTRL_PRESCALE1
#define BF_OST_CTRL_PRESCALE1_V(e) BF_OST_CTRL_PRESCALE1(BV_OST_CTRL_PRESCALE1__##e)
#define BFM_OST_CTRL_PRESCALE1_V(v) BM_OST_CTRL_PRESCALE1
diff --git a/firmware/target/mips/ingenic_x1000/x1000/pcm.h b/firmware/target/mips/ingenic_x1000/x1000/pcm.h
new file mode 100644
index 0000000000..e47a2e5c13
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/x1000/pcm.h
@@ -0,0 +1,251 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * This file was automatically generated by headergen, DO NOT EDIT it.
+ * headergen version: 3.0.0
+ * x1000 version: 1.0
+ * x1000 authors: Aidan MacDonald
+ *
+ * Copyright (C) 2015 by the authors
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef __HEADERGEN_PCM_H__
+#define __HEADERGEN_PCM_H__
+
+#include "macro.h"
+
+#define REG_PCM_CTL jz_reg(PCM_CTL)
+#define JA_PCM_CTL (0xb0071000 + 0x0)
+#define JT_PCM_CTL JIO_32_RW
+#define JN_PCM_CTL PCM_CTL
+#define JI_PCM_CTL
+#define BP_PCM_CTL_ERDMA 9
+#define BM_PCM_CTL_ERDMA 0x200
+#define BF_PCM_CTL_ERDMA(v) (((v) & 0x1) << 9)
+#define BFM_PCM_CTL_ERDMA(v) BM_PCM_CTL_ERDMA
+#define BF_PCM_CTL_ERDMA_V(e) BF_PCM_CTL_ERDMA(BV_PCM_CTL_ERDMA__##e)
+#define BFM_PCM_CTL_ERDMA_V(v) BM_PCM_CTL_ERDMA
+#define BP_PCM_CTL_ETDMA 8
+#define BM_PCM_CTL_ETDMA 0x100
+#define BF_PCM_CTL_ETDMA(v) (((v) & 0x1) << 8)
+#define BFM_PCM_CTL_ETDMA(v) BM_PCM_CTL_ETDMA
+#define BF_PCM_CTL_ETDMA_V(e) BF_PCM_CTL_ETDMA(BV_PCM_CTL_ETDMA__##e)
+#define BFM_PCM_CTL_ETDMA_V(v) BM_PCM_CTL_ETDMA
+#define BP_PCM_CTL_LSMP 7
+#define BM_PCM_CTL_LSMP 0x80
+#define BF_PCM_CTL_LSMP(v) (((v) & 0x1) << 7)
+#define BFM_PCM_CTL_LSMP(v) BM_PCM_CTL_LSMP
+#define BF_PCM_CTL_LSMP_V(e) BF_PCM_CTL_LSMP(BV_PCM_CTL_LSMP__##e)
+#define BFM_PCM_CTL_LSMP_V(v) BM_PCM_CTL_LSMP
+#define BP_PCM_CTL_ERPL 6
+#define BM_PCM_CTL_ERPL 0x40
+#define BF_PCM_CTL_ERPL(v) (((v) & 0x1) << 6)
+#define BFM_PCM_CTL_ERPL(v) BM_PCM_CTL_ERPL
+#define BF_PCM_CTL_ERPL_V(e) BF_PCM_CTL_ERPL(BV_PCM_CTL_ERPL__##e)
+#define BFM_PCM_CTL_ERPL_V(v) BM_PCM_CTL_ERPL
+#define BP_PCM_CTL_EREC 5
+#define BM_PCM_CTL_EREC 0x20
+#define BF_PCM_CTL_EREC(v) (((v) & 0x1) << 5)
+#define BFM_PCM_CTL_EREC(v) BM_PCM_CTL_EREC
+#define BF_PCM_CTL_EREC_V(e) BF_PCM_CTL_EREC(BV_PCM_CTL_EREC__##e)
+#define BFM_PCM_CTL_EREC_V(v) BM_PCM_CTL_EREC
+#define BP_PCM_CTL_FLUSH 4
+#define BM_PCM_CTL_FLUSH 0x10
+#define BF_PCM_CTL_FLUSH(v) (((v) & 0x1) << 4)
+#define BFM_PCM_CTL_FLUSH(v) BM_PCM_CTL_FLUSH
+#define BF_PCM_CTL_FLUSH_V(e) BF_PCM_CTL_FLUSH(BV_PCM_CTL_FLUSH__##e)
+#define BFM_PCM_CTL_FLUSH_V(v) BM_PCM_CTL_FLUSH
+#define BP_PCM_CTL_RST 3
+#define BM_PCM_CTL_RST 0x8
+#define BF_PCM_CTL_RST(v) (((v) & 0x1) << 3)
+#define BFM_PCM_CTL_RST(v) BM_PCM_CTL_RST
+#define BF_PCM_CTL_RST_V(e) BF_PCM_CTL_RST(BV_PCM_CTL_RST__##e)
+#define BFM_PCM_CTL_RST_V(v) BM_PCM_CTL_RST
+#define BP_PCM_CTL_CLKEN 1
+#define BM_PCM_CTL_CLKEN 0x2
+#define BF_PCM_CTL_CLKEN(v) (((v) & 0x1) << 1)
+#define BFM_PCM_CTL_CLKEN(v) BM_PCM_CTL_CLKEN
+#define BF_PCM_CTL_CLKEN_V(e) BF_PCM_CTL_CLKEN(BV_PCM_CTL_CLKEN__##e)
+#define BFM_PCM_CTL_CLKEN_V(v) BM_PCM_CTL_CLKEN
+#define BP_PCM_CTL_PCMEN 0
+#define BM_PCM_CTL_PCMEN 0x1
+#define BF_PCM_CTL_PCMEN(v) (((v) & 0x1) << 0)
+#define BFM_PCM_CTL_PCMEN(v) BM_PCM_CTL_PCMEN
+#define BF_PCM_CTL_PCMEN_V(e) BF_PCM_CTL_PCMEN(BV_PCM_CTL_PCMEN__##e)
+#define BFM_PCM_CTL_PCMEN_V(v) BM_PCM_CTL_PCMEN
+
+#define REG_PCM_CFG jz_reg(PCM_CFG)
+#define JA_PCM_CFG (0xb0071000 + 0x4)
+#define JT_PCM_CFG JIO_32_RW
+#define JN_PCM_CFG PCM_CFG
+#define JI_PCM_CFG
+#define BP_PCM_CFG_SLOT 13
+#define BM_PCM_CFG_SLOT 0x6000
+#define BF_PCM_CFG_SLOT(v) (((v) & 0x3) << 13)
+#define BFM_PCM_CFG_SLOT(v) BM_PCM_CFG_SLOT
+#define BF_PCM_CFG_SLOT_V(e) BF_PCM_CFG_SLOT(BV_PCM_CFG_SLOT__##e)
+#define BFM_PCM_CFG_SLOT_V(v) BM_PCM_CFG_SLOT
+#define BP_PCM_CFG_RFTH 5
+#define BM_PCM_CFG_RFTH 0x1e0
+#define BF_PCM_CFG_RFTH(v) (((v) & 0xf) << 5)
+#define BFM_PCM_CFG_RFTH(v) BM_PCM_CFG_RFTH
+#define BF_PCM_CFG_RFTH_V(e) BF_PCM_CFG_RFTH(BV_PCM_CFG_RFTH__##e)
+#define BFM_PCM_CFG_RFTH_V(v) BM_PCM_CFG_RFTH
+#define BP_PCM_CFG_TFTH 1
+#define BM_PCM_CFG_TFTH 0x1e
+#define BF_PCM_CFG_TFTH(v) (((v) & 0xf) << 1)
+#define BFM_PCM_CFG_TFTH(v) BM_PCM_CFG_TFTH
+#define BF_PCM_CFG_TFTH_V(e) BF_PCM_CFG_TFTH(BV_PCM_CFG_TFTH__##e)
+#define BFM_PCM_CFG_TFTH_V(v) BM_PCM_CFG_TFTH
+#define BP_PCM_CFG_ISS 12
+#define BM_PCM_CFG_ISS 0x1000
+#define BF_PCM_CFG_ISS(v) (((v) & 0x1) << 12)
+#define BFM_PCM_CFG_ISS(v) BM_PCM_CFG_ISS
+#define BF_PCM_CFG_ISS_V(e) BF_PCM_CFG_ISS(BV_PCM_CFG_ISS__##e)
+#define BFM_PCM_CFG_ISS_V(v) BM_PCM_CFG_ISS
+#define BP_PCM_CFG_OSS 11
+#define BM_PCM_CFG_OSS 0x800
+#define BF_PCM_CFG_OSS(v) (((v) & 0x1) << 11)
+#define BFM_PCM_CFG_OSS(v) BM_PCM_CFG_OSS
+#define BF_PCM_CFG_OSS_V(e) BF_PCM_CFG_OSS(BV_PCM_CFG_OSS__##e)
+#define BFM_PCM_CFG_OSS_V(v) BM_PCM_CFG_OSS
+#define BP_PCM_CFG_IMSBPOS 10
+#define BM_PCM_CFG_IMSBPOS 0x400
+#define BF_PCM_CFG_IMSBPOS(v) (((v) & 0x1) << 10)
+#define BFM_PCM_CFG_IMSBPOS(v) BM_PCM_CFG_IMSBPOS
+#define BF_PCM_CFG_IMSBPOS_V(e) BF_PCM_CFG_IMSBPOS(BV_PCM_CFG_IMSBPOS__##e)
+#define BFM_PCM_CFG_IMSBPOS_V(v) BM_PCM_CFG_IMSBPOS
+#define BP_PCM_CFG_OMSBPOS 9
+#define BM_PCM_CFG_OMSBPOS 0x200
+#define BF_PCM_CFG_OMSBPOS(v) (((v) & 0x1) << 9)
+#define BFM_PCM_CFG_OMSBPOS(v) BM_PCM_CFG_OMSBPOS
+#define BF_PCM_CFG_OMSBPOS_V(e) BF_PCM_CFG_OMSBPOS(BV_PCM_CFG_OMSBPOS__##e)
+#define BFM_PCM_CFG_OMSBPOS_V(v) BM_PCM_CFG_OMSBPOS
+#define BP_PCM_CFG_PCMMOD 0
+#define BM_PCM_CFG_PCMMOD 0x1
+#define BF_PCM_CFG_PCMMOD(v) (((v) & 0x1) << 0)
+#define BFM_PCM_CFG_PCMMOD(v) BM_PCM_CFG_PCMMOD
+#define BF_PCM_CFG_PCMMOD_V(e) BF_PCM_CFG_PCMMOD(BV_PCM_CFG_PCMMOD__##e)
+#define BFM_PCM_CFG_PCMMOD_V(v) BM_PCM_CFG_PCMMOD
+
+#define REG_PCM_DP jz_reg(PCM_DP)
+#define JA_PCM_DP (0xb0071000 + 0x8)
+#define JT_PCM_DP JIO_32_RW
+#define JN_PCM_DP PCM_DP
+#define JI_PCM_DP
+
+#define REG_PCM_INTC jz_reg(PCM_INTC)
+#define JA_PCM_INTC (0xb0071000 + 0xc)
+#define JT_PCM_INTC JIO_32_RW
+#define JN_PCM_INTC PCM_INTC
+#define JI_PCM_INTC
+#define BP_PCM_INTC_ETFS 3
+#define BM_PCM_INTC_ETFS 0x8
+#define BF_PCM_INTC_ETFS(v) (((v) & 0x1) << 3)
+#define BFM_PCM_INTC_ETFS(v) BM_PCM_INTC_ETFS
+#define BF_PCM_INTC_ETFS_V(e) BF_PCM_INTC_ETFS(BV_PCM_INTC_ETFS__##e)
+#define BFM_PCM_INTC_ETFS_V(v) BM_PCM_INTC_ETFS
+#define BP_PCM_INTC_ETUR 2
+#define BM_PCM_INTC_ETUR 0x4
+#define BF_PCM_INTC_ETUR(v) (((v) & 0x1) << 2)
+#define BFM_PCM_INTC_ETUR(v) BM_PCM_INTC_ETUR
+#define BF_PCM_INTC_ETUR_V(e) BF_PCM_INTC_ETUR(BV_PCM_INTC_ETUR__##e)
+#define BFM_PCM_INTC_ETUR_V(v) BM_PCM_INTC_ETUR
+#define BP_PCM_INTC_ERFS 1
+#define BM_PCM_INTC_ERFS 0x2
+#define BF_PCM_INTC_ERFS(v) (((v) & 0x1) << 1)
+#define BFM_PCM_INTC_ERFS(v) BM_PCM_INTC_ERFS
+#define BF_PCM_INTC_ERFS_V(e) BF_PCM_INTC_ERFS(BV_PCM_INTC_ERFS__##e)
+#define BFM_PCM_INTC_ERFS_V(v) BM_PCM_INTC_ERFS
+#define BP_PCM_INTC_EROR 0
+#define BM_PCM_INTC_EROR 0x1
+#define BF_PCM_INTC_EROR(v) (((v) & 0x1) << 0)
+#define BFM_PCM_INTC_EROR(v) BM_PCM_INTC_EROR
+#define BF_PCM_INTC_EROR_V(e) BF_PCM_INTC_EROR(BV_PCM_INTC_EROR__##e)
+#define BFM_PCM_INTC_EROR_V(v) BM_PCM_INTC_EROR
+
+#define REG_PCM_INTS jz_reg(PCM_INTS)
+#define JA_PCM_INTS (0xb0071000 + 0x10)
+#define JT_PCM_INTS JIO_32_RW
+#define JN_PCM_INTS PCM_INTS
+#define JI_PCM_INTS
+#define BP_PCM_INTS_TFL 9
+#define BM_PCM_INTS_TFL 0x3e00
+#define BF_PCM_INTS_TFL(v) (((v) & 0x1f) << 9)
+#define BFM_PCM_INTS_TFL(v) BM_PCM_INTS_TFL
+#define BF_PCM_INTS_TFL_V(e) BF_PCM_INTS_TFL(BV_PCM_INTS_TFL__##e)
+#define BFM_PCM_INTS_TFL_V(v) BM_PCM_INTS_TFL
+#define BP_PCM_INTS_RSTS 14
+#define BM_PCM_INTS_RSTS 0x4000
+#define BF_PCM_INTS_RSTS(v) (((v) & 0x1) << 14)
+#define BFM_PCM_INTS_RSTS(v) BM_PCM_INTS_RSTS
+#define BF_PCM_INTS_RSTS_V(e) BF_PCM_INTS_RSTS(BV_PCM_INTS_RSTS__##e)
+#define BFM_PCM_INTS_RSTS_V(v) BM_PCM_INTS_RSTS
+#define BP_PCM_INTS_TFS 8
+#define BM_PCM_INTS_TFS 0x100
+#define BF_PCM_INTS_TFS(v) (((v) & 0x1) << 8)
+#define BFM_PCM_INTS_TFS(v) BM_PCM_INTS_TFS
+#define BF_PCM_INTS_TFS_V(e) BF_PCM_INTS_TFS(BV_PCM_INTS_TFS__##e)
+#define BFM_PCM_INTS_TFS_V(v) BM_PCM_INTS_TFS
+#define BP_PCM_INTS_TUR 7
+#define BM_PCM_INTS_TUR 0x80
+#define BF_PCM_INTS_TUR(v) (((v) & 0x1) << 7)
+#define BFM_PCM_INTS_TUR(v) BM_PCM_INTS_TUR
+#define BF_PCM_INTS_TUR_V(e) BF_PCM_INTS_TUR(BV_PCM_INTS_TUR__##e)
+#define BFM_PCM_INTS_TUR_V(v) BM_PCM_INTS_TUR
+#define BP_PCM_INTS_RFL 2
+#define BM_PCM_INTS_RFL 0x7c
+#define BF_PCM_INTS_RFL(v) (((v) & 0x1f) << 2)
+#define BFM_PCM_INTS_RFL(v) BM_PCM_INTS_RFL
+#define BF_PCM_INTS_RFL_V(e) BF_PCM_INTS_RFL(BV_PCM_INTS_RFL__##e)
+#define BFM_PCM_INTS_RFL_V(v) BM_PCM_INTS_RFL
+#define BP_PCM_INTS_RFS 1
+#define BM_PCM_INTS_RFS 0x2
+#define BF_PCM_INTS_RFS(v) (((v) & 0x1) << 1)
+#define BFM_PCM_INTS_RFS(v) BM_PCM_INTS_RFS
+#define BF_PCM_INTS_RFS_V(e) BF_PCM_INTS_RFS(BV_PCM_INTS_RFS__##e)
+#define BFM_PCM_INTS_RFS_V(v) BM_PCM_INTS_RFS
+#define BP_PCM_INTS_ROR 0
+#define BM_PCM_INTS_ROR 0x1
+#define BF_PCM_INTS_ROR(v) (((v) & 0x1) << 0)
+#define BFM_PCM_INTS_ROR(v) BM_PCM_INTS_ROR
+#define BF_PCM_INTS_ROR_V(e) BF_PCM_INTS_ROR(BV_PCM_INTS_ROR__##e)
+#define BFM_PCM_INTS_ROR_V(v) BM_PCM_INTS_ROR
+
+#define REG_PCM_DIV jz_reg(PCM_DIV)
+#define JA_PCM_DIV (0xb0071000 + 0x14)
+#define JT_PCM_DIV JIO_32_RW
+#define JN_PCM_DIV PCM_DIV
+#define JI_PCM_DIV
+#define BP_PCM_DIV_SYNL 11
+#define BM_PCM_DIV_SYNL 0x1f800
+#define BF_PCM_DIV_SYNL(v) (((v) & 0x3f) << 11)
+#define BFM_PCM_DIV_SYNL(v) BM_PCM_DIV_SYNL
+#define BF_PCM_DIV_SYNL_V(e) BF_PCM_DIV_SYNL(BV_PCM_DIV_SYNL__##e)
+#define BFM_PCM_DIV_SYNL_V(v) BM_PCM_DIV_SYNL
+#define BP_PCM_DIV_SYNDIV 6
+#define BM_PCM_DIV_SYNDIV 0x7c0
+#define BF_PCM_DIV_SYNDIV(v) (((v) & 0x1f) << 6)
+#define BFM_PCM_DIV_SYNDIV(v) BM_PCM_DIV_SYNDIV
+#define BF_PCM_DIV_SYNDIV_V(e) BF_PCM_DIV_SYNDIV(BV_PCM_DIV_SYNDIV__##e)
+#define BFM_PCM_DIV_SYNDIV_V(v) BM_PCM_DIV_SYNDIV
+#define BP_PCM_DIV_CLKDIV 0
+#define BM_PCM_DIV_CLKDIV 0x3f
+#define BF_PCM_DIV_CLKDIV(v) (((v) & 0x3f) << 0)
+#define BFM_PCM_DIV_CLKDIV(v) BM_PCM_DIV_CLKDIV
+#define BF_PCM_DIV_CLKDIV_V(e) BF_PCM_DIV_CLKDIV(BV_PCM_DIV_CLKDIV__##e)
+#define BFM_PCM_DIV_CLKDIV_V(v) BM_PCM_DIV_CLKDIV
+
+#endif /* __HEADERGEN_PCM_H__*/
diff --git a/firmware/target/mips/ingenic_x1000/x1000/ssi.h b/firmware/target/mips/ingenic_x1000/x1000/ssi.h
new file mode 100644
index 0000000000..731e73c3a6
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/x1000/ssi.h
@@ -0,0 +1,323 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * This file was automatically generated by headergen, DO NOT EDIT it.
+ * headergen version: 3.0.0
+ * x1000 version: 1.0
+ * x1000 authors: Aidan MacDonald
+ *
+ * Copyright (C) 2015 by the authors
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef __HEADERGEN_SSI_H__
+#define __HEADERGEN_SSI_H__
+
+#include "macro.h"
+
+#define REG_SSI_DR jz_reg(SSI_DR)
+#define JA_SSI_DR (0xb0043000 + 0x0)
+#define JT_SSI_DR JIO_32_RW
+#define JN_SSI_DR SSI_DR
+#define JI_SSI_DR
+
+#define REG_SSI_CR0 jz_reg(SSI_CR0)
+#define JA_SSI_CR0 (0xb0043000 + 0x4)
+#define JT_SSI_CR0 JIO_32_RW
+#define JN_SSI_CR0 SSI_CR0
+#define JI_SSI_CR0
+#define BP_SSI_CR0_TENDIAN 18
+#define BM_SSI_CR0_TENDIAN 0xc0000
+#define BF_SSI_CR0_TENDIAN(v) (((v) & 0x3) << 18)
+#define BFM_SSI_CR0_TENDIAN(v) BM_SSI_CR0_TENDIAN
+#define BF_SSI_CR0_TENDIAN_V(e) BF_SSI_CR0_TENDIAN(BV_SSI_CR0_TENDIAN__##e)
+#define BFM_SSI_CR0_TENDIAN_V(v) BM_SSI_CR0_TENDIAN
+#define BP_SSI_CR0_RENDIAN 16
+#define BM_SSI_CR0_RENDIAN 0x30000
+#define BF_SSI_CR0_RENDIAN(v) (((v) & 0x3) << 16)
+#define BFM_SSI_CR0_RENDIAN(v) BM_SSI_CR0_RENDIAN
+#define BF_SSI_CR0_RENDIAN_V(e) BF_SSI_CR0_RENDIAN(BV_SSI_CR0_RENDIAN__##e)
+#define BFM_SSI_CR0_RENDIAN_V(v) BM_SSI_CR0_RENDIAN
+#define BP_SSI_CR0_SSIE 15
+#define BM_SSI_CR0_SSIE 0x8000
+#define BF_SSI_CR0_SSIE(v) (((v) & 0x1) << 15)
+#define BFM_SSI_CR0_SSIE(v) BM_SSI_CR0_SSIE
+#define BF_SSI_CR0_SSIE_V(e) BF_SSI_CR0_SSIE(BV_SSI_CR0_SSIE__##e)
+#define BFM_SSI_CR0_SSIE_V(v) BM_SSI_CR0_SSIE
+#define BP_SSI_CR0_TIE 14
+#define BM_SSI_CR0_TIE 0x4000
+#define BF_SSI_CR0_TIE(v) (((v) & 0x1) << 14)
+#define BFM_SSI_CR0_TIE(v) BM_SSI_CR0_TIE
+#define BF_SSI_CR0_TIE_V(e) BF_SSI_CR0_TIE(BV_SSI_CR0_TIE__##e)
+#define BFM_SSI_CR0_TIE_V(v) BM_SSI_CR0_TIE
+#define BP_SSI_CR0_RIE 13
+#define BM_SSI_CR0_RIE 0x2000
+#define BF_SSI_CR0_RIE(v) (((v) & 0x1) << 13)
+#define BFM_SSI_CR0_RIE(v) BM_SSI_CR0_RIE
+#define BF_SSI_CR0_RIE_V(e) BF_SSI_CR0_RIE(BV_SSI_CR0_RIE__##e)
+#define BFM_SSI_CR0_RIE_V(v) BM_SSI_CR0_RIE
+#define BP_SSI_CR0_TEIE 12
+#define BM_SSI_CR0_TEIE 0x1000
+#define BF_SSI_CR0_TEIE(v) (((v) & 0x1) << 12)
+#define BFM_SSI_CR0_TEIE(v) BM_SSI_CR0_TEIE
+#define BF_SSI_CR0_TEIE_V(e) BF_SSI_CR0_TEIE(BV_SSI_CR0_TEIE__##e)
+#define BFM_SSI_CR0_TEIE_V(v) BM_SSI_CR0_TEIE
+#define BP_SSI_CR0_REIE 11
+#define BM_SSI_CR0_REIE 0x800
+#define BF_SSI_CR0_REIE(v) (((v) & 0x1) << 11)
+#define BFM_SSI_CR0_REIE(v) BM_SSI_CR0_REIE
+#define BF_SSI_CR0_REIE_V(e) BF_SSI_CR0_REIE(BV_SSI_CR0_REIE__##e)
+#define BFM_SSI_CR0_REIE_V(v) BM_SSI_CR0_REIE
+#define BP_SSI_CR0_LOOP 10
+#define BM_SSI_CR0_LOOP 0x400
+#define BF_SSI_CR0_LOOP(v) (((v) & 0x1) << 10)
+#define BFM_SSI_CR0_LOOP(v) BM_SSI_CR0_LOOP
+#define BF_SSI_CR0_LOOP_V(e) BF_SSI_CR0_LOOP(BV_SSI_CR0_LOOP__##e)
+#define BFM_SSI_CR0_LOOP_V(v) BM_SSI_CR0_LOOP
+#define BP_SSI_CR0_RFINE 9
+#define BM_SSI_CR0_RFINE 0x200
+#define BF_SSI_CR0_RFINE(v) (((v) & 0x1) << 9)
+#define BFM_SSI_CR0_RFINE(v) BM_SSI_CR0_RFINE
+#define BF_SSI_CR0_RFINE_V(e) BF_SSI_CR0_RFINE(BV_SSI_CR0_RFINE__##e)
+#define BFM_SSI_CR0_RFINE_V(v) BM_SSI_CR0_RFINE
+#define BP_SSI_CR0_RFINC 8
+#define BM_SSI_CR0_RFINC 0x100
+#define BF_SSI_CR0_RFINC(v) (((v) & 0x1) << 8)
+#define BFM_SSI_CR0_RFINC(v) BM_SSI_CR0_RFINC
+#define BF_SSI_CR0_RFINC_V(e) BF_SSI_CR0_RFINC(BV_SSI_CR0_RFINC__##e)
+#define BFM_SSI_CR0_RFINC_V(v) BM_SSI_CR0_RFINC
+#define BP_SSI_CR0_EACLRUN 7
+#define BM_SSI_CR0_EACLRUN 0x80
+#define BF_SSI_CR0_EACLRUN(v) (((v) & 0x1) << 7)
+#define BFM_SSI_CR0_EACLRUN(v) BM_SSI_CR0_EACLRUN
+#define BF_SSI_CR0_EACLRUN_V(e) BF_SSI_CR0_EACLRUN(BV_SSI_CR0_EACLRUN__##e)
+#define BFM_SSI_CR0_EACLRUN_V(v) BM_SSI_CR0_EACLRUN
+#define BP_SSI_CR0_FSEL 6
+#define BM_SSI_CR0_FSEL 0x40
+#define BF_SSI_CR0_FSEL(v) (((v) & 0x1) << 6)
+#define BFM_SSI_CR0_FSEL(v) BM_SSI_CR0_FSEL
+#define BF_SSI_CR0_FSEL_V(e) BF_SSI_CR0_FSEL(BV_SSI_CR0_FSEL__##e)
+#define BFM_SSI_CR0_FSEL_V(v) BM_SSI_CR0_FSEL
+#define BP_SSI_CR0_VRCNT 4
+#define BM_SSI_CR0_VRCNT 0x10
+#define BF_SSI_CR0_VRCNT(v) (((v) & 0x1) << 4)
+#define BFM_SSI_CR0_VRCNT(v) BM_SSI_CR0_VRCNT
+#define BF_SSI_CR0_VRCNT_V(e) BF_SSI_CR0_VRCNT(BV_SSI_CR0_VRCNT__##e)
+#define BFM_SSI_CR0_VRCNT_V(v) BM_SSI_CR0_VRCNT
+#define BP_SSI_CR0_TFMODE 3
+#define BM_SSI_CR0_TFMODE 0x8
+#define BF_SSI_CR0_TFMODE(v) (((v) & 0x1) << 3)
+#define BFM_SSI_CR0_TFMODE(v) BM_SSI_CR0_TFMODE
+#define BF_SSI_CR0_TFMODE_V(e) BF_SSI_CR0_TFMODE(BV_SSI_CR0_TFMODE__##e)
+#define BFM_SSI_CR0_TFMODE_V(v) BM_SSI_CR0_TFMODE
+#define BP_SSI_CR0_TFLUSH 2
+#define BM_SSI_CR0_TFLUSH 0x4
+#define BF_SSI_CR0_TFLUSH(v) (((v) & 0x1) << 2)
+#define BFM_SSI_CR0_TFLUSH(v) BM_SSI_CR0_TFLUSH
+#define BF_SSI_CR0_TFLUSH_V(e) BF_SSI_CR0_TFLUSH(BV_SSI_CR0_TFLUSH__##e)
+#define BFM_SSI_CR0_TFLUSH_V(v) BM_SSI_CR0_TFLUSH
+#define BP_SSI_CR0_RFLUSH 1
+#define BM_SSI_CR0_RFLUSH 0x2
+#define BF_SSI_CR0_RFLUSH(v) (((v) & 0x1) << 1)
+#define BFM_SSI_CR0_RFLUSH(v) BM_SSI_CR0_RFLUSH
+#define BF_SSI_CR0_RFLUSH_V(e) BF_SSI_CR0_RFLUSH(BV_SSI_CR0_RFLUSH__##e)
+#define BFM_SSI_CR0_RFLUSH_V(v) BM_SSI_CR0_RFLUSH
+#define BP_SSI_CR0_DISREV 0
+#define BM_SSI_CR0_DISREV 0x1
+#define BF_SSI_CR0_DISREV(v) (((v) & 0x1) << 0)
+#define BFM_SSI_CR0_DISREV(v) BM_SSI_CR0_DISREV
+#define BF_SSI_CR0_DISREV_V(e) BF_SSI_CR0_DISREV(BV_SSI_CR0_DISREV__##e)
+#define BFM_SSI_CR0_DISREV_V(v) BM_SSI_CR0_DISREV
+
+#define REG_SSI_CR1 jz_reg(SSI_CR1)
+#define JA_SSI_CR1 (0xb0043000 + 0x8)
+#define JT_SSI_CR1 JIO_32_RW
+#define JN_SSI_CR1 SSI_CR1
+#define JI_SSI_CR1
+#define BP_SSI_CR1_FRMHL 30
+#define BM_SSI_CR1_FRMHL 0xc0000000
+#define BF_SSI_CR1_FRMHL(v) (((v) & 0x3) << 30)
+#define BFM_SSI_CR1_FRMHL(v) BM_SSI_CR1_FRMHL
+#define BF_SSI_CR1_FRMHL_V(e) BF_SSI_CR1_FRMHL(BV_SSI_CR1_FRMHL__##e)
+#define BFM_SSI_CR1_FRMHL_V(v) BM_SSI_CR1_FRMHL
+#define BP_SSI_CR1_TFVCK 28
+#define BM_SSI_CR1_TFVCK 0x30000000
+#define BF_SSI_CR1_TFVCK(v) (((v) & 0x3) << 28)
+#define BFM_SSI_CR1_TFVCK(v) BM_SSI_CR1_TFVCK
+#define BF_SSI_CR1_TFVCK_V(e) BF_SSI_CR1_TFVCK(BV_SSI_CR1_TFVCK__##e)
+#define BFM_SSI_CR1_TFVCK_V(v) BM_SSI_CR1_TFVCK
+#define BP_SSI_CR1_TCKFI 26
+#define BM_SSI_CR1_TCKFI 0xc000000
+#define BF_SSI_CR1_TCKFI(v) (((v) & 0x3) << 26)
+#define BFM_SSI_CR1_TCKFI(v) BM_SSI_CR1_TCKFI
+#define BF_SSI_CR1_TCKFI_V(e) BF_SSI_CR1_TCKFI(BV_SSI_CR1_TCKFI__##e)
+#define BFM_SSI_CR1_TCKFI_V(v) BM_SSI_CR1_TCKFI
+#define BP_SSI_CR1_FMAT 20
+#define BM_SSI_CR1_FMAT 0x300000
+#define BF_SSI_CR1_FMAT(v) (((v) & 0x3) << 20)
+#define BFM_SSI_CR1_FMAT(v) BM_SSI_CR1_FMAT
+#define BF_SSI_CR1_FMAT_V(e) BF_SSI_CR1_FMAT(BV_SSI_CR1_FMAT__##e)
+#define BFM_SSI_CR1_FMAT_V(v) BM_SSI_CR1_FMAT
+#define BP_SSI_CR1_TTRG 16
+#define BM_SSI_CR1_TTRG 0xf0000
+#define BF_SSI_CR1_TTRG(v) (((v) & 0xf) << 16)
+#define BFM_SSI_CR1_TTRG(v) BM_SSI_CR1_TTRG
+#define BF_SSI_CR1_TTRG_V(e) BF_SSI_CR1_TTRG(BV_SSI_CR1_TTRG__##e)
+#define BFM_SSI_CR1_TTRG_V(v) BM_SSI_CR1_TTRG
+#define BP_SSI_CR1_MCOM 12
+#define BM_SSI_CR1_MCOM 0xf000
+#define BF_SSI_CR1_MCOM(v) (((v) & 0xf) << 12)
+#define BFM_SSI_CR1_MCOM(v) BM_SSI_CR1_MCOM
+#define BF_SSI_CR1_MCOM_V(e) BF_SSI_CR1_MCOM(BV_SSI_CR1_MCOM__##e)
+#define BFM_SSI_CR1_MCOM_V(v) BM_SSI_CR1_MCOM
+#define BP_SSI_CR1_RTRG 8
+#define BM_SSI_CR1_RTRG 0xf00
+#define BF_SSI_CR1_RTRG(v) (((v) & 0xf) << 8)
+#define BFM_SSI_CR1_RTRG(v) BM_SSI_CR1_RTRG
+#define BF_SSI_CR1_RTRG_V(e) BF_SSI_CR1_RTRG(BV_SSI_CR1_RTRG__##e)
+#define BFM_SSI_CR1_RTRG_V(v) BM_SSI_CR1_RTRG
+#define BP_SSI_CR1_FLEN 3
+#define BM_SSI_CR1_FLEN 0xf8
+#define BF_SSI_CR1_FLEN(v) (((v) & 0x1f) << 3)
+#define BFM_SSI_CR1_FLEN(v) BM_SSI_CR1_FLEN
+#define BF_SSI_CR1_FLEN_V(e) BF_SSI_CR1_FLEN(BV_SSI_CR1_FLEN__##e)
+#define BFM_SSI_CR1_FLEN_V(v) BM_SSI_CR1_FLEN
+#define BP_SSI_CR1_ITFRM 24
+#define BM_SSI_CR1_ITFRM 0x1000000
+#define BF_SSI_CR1_ITFRM(v) (((v) & 0x1) << 24)
+#define BFM_SSI_CR1_ITFRM(v) BM_SSI_CR1_ITFRM
+#define BF_SSI_CR1_ITFRM_V(e) BF_SSI_CR1_ITFRM(BV_SSI_CR1_ITFRM__##e)
+#define BFM_SSI_CR1_ITFRM_V(v) BM_SSI_CR1_ITFRM
+#define BP_SSI_CR1_UNFIN 23
+#define BM_SSI_CR1_UNFIN 0x800000
+#define BF_SSI_CR1_UNFIN(v) (((v) & 0x1) << 23)
+#define BFM_SSI_CR1_UNFIN(v) BM_SSI_CR1_UNFIN
+#define BF_SSI_CR1_UNFIN_V(e) BF_SSI_CR1_UNFIN(BV_SSI_CR1_UNFIN__##e)
+#define BFM_SSI_CR1_UNFIN_V(v) BM_SSI_CR1_UNFIN
+#define BP_SSI_CR1_PHA 1
+#define BM_SSI_CR1_PHA 0x2
+#define BF_SSI_CR1_PHA(v) (((v) & 0x1) << 1)
+#define BFM_SSI_CR1_PHA(v) BM_SSI_CR1_PHA
+#define BF_SSI_CR1_PHA_V(e) BF_SSI_CR1_PHA(BV_SSI_CR1_PHA__##e)
+#define BFM_SSI_CR1_PHA_V(v) BM_SSI_CR1_PHA
+#define BP_SSI_CR1_POL 0
+#define BM_SSI_CR1_POL 0x1
+#define BF_SSI_CR1_POL(v) (((v) & 0x1) << 0)
+#define BFM_SSI_CR1_POL(v) BM_SSI_CR1_POL
+#define BF_SSI_CR1_POL_V(e) BF_SSI_CR1_POL(BV_SSI_CR1_POL__##e)
+#define BFM_SSI_CR1_POL_V(v) BM_SSI_CR1_POL
+
+#define REG_SSI_SR jz_reg(SSI_SR)
+#define JA_SSI_SR (0xb0043000 + 0xc)
+#define JT_SSI_SR JIO_32_RW
+#define JN_SSI_SR SSI_SR
+#define JI_SSI_SR
+#define BP_SSI_SR_TFIFO_NUM 16
+#define BM_SSI_SR_TFIFO_NUM 0x1ff0000
+#define BF_SSI_SR_TFIFO_NUM(v) (((v) & 0x1ff) << 16)
+#define BFM_SSI_SR_TFIFO_NUM(v) BM_SSI_SR_TFIFO_NUM
+#define BF_SSI_SR_TFIFO_NUM_V(e) BF_SSI_SR_TFIFO_NUM(BV_SSI_SR_TFIFO_NUM__##e)
+#define BFM_SSI_SR_TFIFO_NUM_V(v) BM_SSI_SR_TFIFO_NUM
+#define BP_SSI_SR_RFIFO_NUM 8
+#define BM_SSI_SR_RFIFO_NUM 0xff00
+#define BF_SSI_SR_RFIFO_NUM(v) (((v) & 0xff) << 8)
+#define BFM_SSI_SR_RFIFO_NUM(v) BM_SSI_SR_RFIFO_NUM
+#define BF_SSI_SR_RFIFO_NUM_V(e) BF_SSI_SR_RFIFO_NUM(BV_SSI_SR_RFIFO_NUM__##e)
+#define BFM_SSI_SR_RFIFO_NUM_V(v) BM_SSI_SR_RFIFO_NUM
+#define BP_SSI_SR_END 7
+#define BM_SSI_SR_END 0x80
+#define BF_SSI_SR_END(v) (((v) & 0x1) << 7)
+#define BFM_SSI_SR_END(v) BM_SSI_SR_END
+#define BF_SSI_SR_END_V(e) BF_SSI_SR_END(BV_SSI_SR_END__##e)
+#define BFM_SSI_SR_END_V(v) BM_SSI_SR_END
+#define BP_SSI_SR_BUSY 6
+#define BM_SSI_SR_BUSY 0x40
+#define BF_SSI_SR_BUSY(v) (((v) & 0x1) << 6)
+#define BFM_SSI_SR_BUSY(v) BM_SSI_SR_BUSY
+#define BF_SSI_SR_BUSY_V(e) BF_SSI_SR_BUSY(BV_SSI_SR_BUSY__##e)
+#define BFM_SSI_SR_BUSY_V(v) BM_SSI_SR_BUSY
+#define BP_SSI_SR_TFF 5
+#define BM_SSI_SR_TFF 0x20
+#define BF_SSI_SR_TFF(v) (((v) & 0x1) << 5)
+#define BFM_SSI_SR_TFF(v) BM_SSI_SR_TFF
+#define BF_SSI_SR_TFF_V(e) BF_SSI_SR_TFF(BV_SSI_SR_TFF__##e)
+#define BFM_SSI_SR_TFF_V(v) BM_SSI_SR_TFF
+#define BP_SSI_SR_RFE 4
+#define BM_SSI_SR_RFE 0x10
+#define BF_SSI_SR_RFE(v) (((v) & 0x1) << 4)
+#define BFM_SSI_SR_RFE(v) BM_SSI_SR_RFE
+#define BF_SSI_SR_RFE_V(e) BF_SSI_SR_RFE(BV_SSI_SR_RFE__##e)
+#define BFM_SSI_SR_RFE_V(v) BM_SSI_SR_RFE
+#define BP_SSI_SR_TFHE 3
+#define BM_SSI_SR_TFHE 0x8
+#define BF_SSI_SR_TFHE(v) (((v) & 0x1) << 3)
+#define BFM_SSI_SR_TFHE(v) BM_SSI_SR_TFHE
+#define BF_SSI_SR_TFHE_V(e) BF_SSI_SR_TFHE(BV_SSI_SR_TFHE__##e)
+#define BFM_SSI_SR_TFHE_V(v) BM_SSI_SR_TFHE
+#define BP_SSI_SR_RFHF 2
+#define BM_SSI_SR_RFHF 0x4
+#define BF_SSI_SR_RFHF(v) (((v) & 0x1) << 2)
+#define BFM_SSI_SR_RFHF(v) BM_SSI_SR_RFHF
+#define BF_SSI_SR_RFHF_V(e) BF_SSI_SR_RFHF(BV_SSI_SR_RFHF__##e)
+#define BFM_SSI_SR_RFHF_V(v) BM_SSI_SR_RFHF
+#define BP_SSI_SR_UNDR 1
+#define BM_SSI_SR_UNDR 0x2
+#define BF_SSI_SR_UNDR(v) (((v) & 0x1) << 1)
+#define BFM_SSI_SR_UNDR(v) BM_SSI_SR_UNDR
+#define BF_SSI_SR_UNDR_V(e) BF_SSI_SR_UNDR(BV_SSI_SR_UNDR__##e)
+#define BFM_SSI_SR_UNDR_V(v) BM_SSI_SR_UNDR
+#define BP_SSI_SR_OVER 0
+#define BM_SSI_SR_OVER 0x1
+#define BF_SSI_SR_OVER(v) (((v) & 0x1) << 0)
+#define BFM_SSI_SR_OVER(v) BM_SSI_SR_OVER
+#define BF_SSI_SR_OVER_V(e) BF_SSI_SR_OVER(BV_SSI_SR_OVER__##e)
+#define BFM_SSI_SR_OVER_V(v) BM_SSI_SR_OVER
+
+#define REG_SSI_ITR jz_reg(SSI_ITR)
+#define JA_SSI_ITR (0xb0043000 + 0x10)
+#define JT_SSI_ITR JIO_32_RW
+#define JN_SSI_ITR SSI_ITR
+#define JI_SSI_ITR
+#define BP_SSI_ITR_IVLTM 0
+#define BM_SSI_ITR_IVLTM 0x7fff
+#define BF_SSI_ITR_IVLTM(v) (((v) & 0x7fff) << 0)
+#define BFM_SSI_ITR_IVLTM(v) BM_SSI_ITR_IVLTM
+#define BF_SSI_ITR_IVLTM_V(e) BF_SSI_ITR_IVLTM(BV_SSI_ITR_IVLTM__##e)
+#define BFM_SSI_ITR_IVLTM_V(v) BM_SSI_ITR_IVLTM
+#define BP_SSI_ITR_CNTCLK 15
+#define BM_SSI_ITR_CNTCLK 0x8000
+#define BF_SSI_ITR_CNTCLK(v) (((v) & 0x1) << 15)
+#define BFM_SSI_ITR_CNTCLK(v) BM_SSI_ITR_CNTCLK
+#define BF_SSI_ITR_CNTCLK_V(e) BF_SSI_ITR_CNTCLK(BV_SSI_ITR_CNTCLK__##e)
+#define BFM_SSI_ITR_CNTCLK_V(v) BM_SSI_ITR_CNTCLK
+
+#define REG_SSI_ICR jz_reg(SSI_ICR)
+#define JA_SSI_ICR (0xb0043000 + 0x14)
+#define JT_SSI_ICR JIO_32_RW
+#define JN_SSI_ICR SSI_ICR
+#define JI_SSI_ICR
+
+#define REG_SSI_GR jz_reg(SSI_GR)
+#define JA_SSI_GR (0xb0043000 + 0x18)
+#define JT_SSI_GR JIO_32_RW
+#define JN_SSI_GR SSI_GR
+#define JI_SSI_GR
+
+#define REG_SSI_RCNT jz_reg(SSI_RCNT)
+#define JA_SSI_RCNT (0xb0043000 + 0x1c)
+#define JT_SSI_RCNT JIO_32_RW
+#define JN_SSI_RCNT SSI_RCNT
+#define JI_SSI_RCNT
+
+#endif /* __HEADERGEN_SSI_H__*/
diff --git a/firmware/target/mips/ingenic_x1000/x1000/uart.h b/firmware/target/mips/ingenic_x1000/x1000/uart.h
new file mode 100644
index 0000000000..6b42740bb7
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/x1000/uart.h
@@ -0,0 +1,390 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * This file was automatically generated by headergen, DO NOT EDIT it.
+ * headergen version: 3.0.0
+ * x1000 version: 1.0
+ * x1000 authors: Aidan MacDonald
+ *
+ * Copyright (C) 2015 by the authors
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef __HEADERGEN_UART_H__
+#define __HEADERGEN_UART_H__
+
+#include "macro.h"
+
+#define REG_UART_URBR(_n1) jz_reg(UART_URBR(_n1))
+#define JA_UART_URBR(_n1) (0xb0030000 + (_n1) * 0x1000 + 0x0)
+#define JT_UART_URBR(_n1) JIO_32_RW
+#define JN_UART_URBR(_n1) UART_URBR
+#define JI_UART_URBR(_n1) (_n1)
+
+#define REG_UART_UTHR(_n1) jz_reg(UART_UTHR(_n1))
+#define JA_UART_UTHR(_n1) (0xb0030000 + (_n1) * 0x1000 + 0x0)
+#define JT_UART_UTHR(_n1) JIO_32_RW
+#define JN_UART_UTHR(_n1) UART_UTHR
+#define JI_UART_UTHR(_n1) (_n1)
+
+#define REG_UART_UDLLR(_n1) jz_reg(UART_UDLLR(_n1))
+#define JA_UART_UDLLR(_n1) (0xb0030000 + (_n1) * 0x1000 + 0x0)
+#define JT_UART_UDLLR(_n1) JIO_32_RW
+#define JN_UART_UDLLR(_n1) UART_UDLLR
+#define JI_UART_UDLLR(_n1) (_n1)
+
+#define REG_UART_UDLHR(_n1) jz_reg(UART_UDLHR(_n1))
+#define JA_UART_UDLHR(_n1) (0xb0030000 + (_n1) * 0x1000 + 0x4)
+#define JT_UART_UDLHR(_n1) JIO_32_RW
+#define JN_UART_UDLHR(_n1) UART_UDLHR
+#define JI_UART_UDLHR(_n1) (_n1)
+
+#define REG_UART_UIER(_n1) jz_reg(UART_UIER(_n1))
+#define JA_UART_UIER(_n1) (0xb0030000 + (_n1) * 0x1000 + 0x4)
+#define JT_UART_UIER(_n1) JIO_32_RW
+#define JN_UART_UIER(_n1) UART_UIER
+#define JI_UART_UIER(_n1) (_n1)
+#define BP_UART_UIER_RTOIE 4
+#define BM_UART_UIER_RTOIE 0x10
+#define BF_UART_UIER_RTOIE(v) (((v) & 0x1) << 4)
+#define BFM_UART_UIER_RTOIE(v) BM_UART_UIER_RTOIE
+#define BF_UART_UIER_RTOIE_V(e) BF_UART_UIER_RTOIE(BV_UART_UIER_RTOIE__##e)
+#define BFM_UART_UIER_RTOIE_V(v) BM_UART_UIER_RTOIE
+#define BP_UART_UIER_MSIE 3
+#define BM_UART_UIER_MSIE 0x8
+#define BF_UART_UIER_MSIE(v) (((v) & 0x1) << 3)
+#define BFM_UART_UIER_MSIE(v) BM_UART_UIER_MSIE
+#define BF_UART_UIER_MSIE_V(e) BF_UART_UIER_MSIE(BV_UART_UIER_MSIE__##e)
+#define BFM_UART_UIER_MSIE_V(v) BM_UART_UIER_MSIE
+#define BP_UART_UIER_RLSIE 2
+#define BM_UART_UIER_RLSIE 0x4
+#define BF_UART_UIER_RLSIE(v) (((v) & 0x1) << 2)
+#define BFM_UART_UIER_RLSIE(v) BM_UART_UIER_RLSIE
+#define BF_UART_UIER_RLSIE_V(e) BF_UART_UIER_RLSIE(BV_UART_UIER_RLSIE__##e)
+#define BFM_UART_UIER_RLSIE_V(v) BM_UART_UIER_RLSIE
+#define BP_UART_UIER_TDRIE 1
+#define BM_UART_UIER_TDRIE 0x2
+#define BF_UART_UIER_TDRIE(v) (((v) & 0x1) << 1)
+#define BFM_UART_UIER_TDRIE(v) BM_UART_UIER_TDRIE
+#define BF_UART_UIER_TDRIE_V(e) BF_UART_UIER_TDRIE(BV_UART_UIER_TDRIE__##e)
+#define BFM_UART_UIER_TDRIE_V(v) BM_UART_UIER_TDRIE
+#define BP_UART_UIER_RDRIE 0
+#define BM_UART_UIER_RDRIE 0x1
+#define BF_UART_UIER_RDRIE(v) (((v) & 0x1) << 0)
+#define BFM_UART_UIER_RDRIE(v) BM_UART_UIER_RDRIE
+#define BF_UART_UIER_RDRIE_V(e) BF_UART_UIER_RDRIE(BV_UART_UIER_RDRIE__##e)
+#define BFM_UART_UIER_RDRIE_V(v) BM_UART_UIER_RDRIE
+
+#define REG_UART_UIIR(_n1) jz_reg(UART_UIIR(_n1))
+#define JA_UART_UIIR(_n1) (0xb0030000 + (_n1) * 0x1000 + 0x8)
+#define JT_UART_UIIR(_n1) JIO_32_RW
+#define JN_UART_UIIR(_n1) UART_UIIR
+#define JI_UART_UIIR(_n1) (_n1)
+#define BP_UART_UIIR_FFMSEL 6
+#define BM_UART_UIIR_FFMSEL 0xc0
+#define BV_UART_UIIR_FFMSEL__NON_FIFO_MODE 0x0
+#define BV_UART_UIIR_FFMSEL__FIFO_MODE 0x1
+#define BF_UART_UIIR_FFMSEL(v) (((v) & 0x3) << 6)
+#define BFM_UART_UIIR_FFMSEL(v) BM_UART_UIIR_FFMSEL
+#define BF_UART_UIIR_FFMSEL_V(e) BF_UART_UIIR_FFMSEL(BV_UART_UIIR_FFMSEL__##e)
+#define BFM_UART_UIIR_FFMSEL_V(v) BM_UART_UIIR_FFMSEL
+#define BP_UART_UIIR_INID 1
+#define BM_UART_UIIR_INID 0xe
+#define BV_UART_UIIR_INID__MODEM_STATUS 0x0
+#define BV_UART_UIIR_INID__TRANSMIT_DATA_REQ 0x1
+#define BV_UART_UIIR_INID__RECEIVE_DATA_READY 0x2
+#define BV_UART_UIIR_INID__RECEIVE_LINE_STATUS 0x3
+#define BV_UART_UIIR_INID__RECEIVE_TIME_OUT 0x6
+#define BF_UART_UIIR_INID(v) (((v) & 0x7) << 1)
+#define BFM_UART_UIIR_INID(v) BM_UART_UIIR_INID
+#define BF_UART_UIIR_INID_V(e) BF_UART_UIIR_INID(BV_UART_UIIR_INID__##e)
+#define BFM_UART_UIIR_INID_V(v) BM_UART_UIIR_INID
+#define BP_UART_UIIR_INPEND 0
+#define BM_UART_UIIR_INPEND 0x1
+#define BF_UART_UIIR_INPEND(v) (((v) & 0x1) << 0)
+#define BFM_UART_UIIR_INPEND(v) BM_UART_UIIR_INPEND
+#define BF_UART_UIIR_INPEND_V(e) BF_UART_UIIR_INPEND(BV_UART_UIIR_INPEND__##e)
+#define BFM_UART_UIIR_INPEND_V(v) BM_UART_UIIR_INPEND
+
+#define REG_UART_UFCR(_n1) jz_reg(UART_UFCR(_n1))
+#define JA_UART_UFCR(_n1) (0xb0030000 + (_n1) * 0x1000 + 0x8)
+#define JT_UART_UFCR(_n1) JIO_32_RW
+#define JN_UART_UFCR(_n1) UART_UFCR
+#define JI_UART_UFCR(_n1) (_n1)
+#define BP_UART_UFCR_RDTR 6
+#define BM_UART_UFCR_RDTR 0xc0
+#define BV_UART_UFCR_RDTR__1BYTE 0x0
+#define BV_UART_UFCR_RDTR__16BYTE 0x1
+#define BV_UART_UFCR_RDTR__32BYTE 0x2
+#define BV_UART_UFCR_RDTR__60BYTE 0x3
+#define BF_UART_UFCR_RDTR(v) (((v) & 0x3) << 6)
+#define BFM_UART_UFCR_RDTR(v) BM_UART_UFCR_RDTR
+#define BF_UART_UFCR_RDTR_V(e) BF_UART_UFCR_RDTR(BV_UART_UFCR_RDTR__##e)
+#define BFM_UART_UFCR_RDTR_V(v) BM_UART_UFCR_RDTR
+#define BP_UART_UFCR_UME 4
+#define BM_UART_UFCR_UME 0x10
+#define BF_UART_UFCR_UME(v) (((v) & 0x1) << 4)
+#define BFM_UART_UFCR_UME(v) BM_UART_UFCR_UME
+#define BF_UART_UFCR_UME_V(e) BF_UART_UFCR_UME(BV_UART_UFCR_UME__##e)
+#define BFM_UART_UFCR_UME_V(v) BM_UART_UFCR_UME
+#define BP_UART_UFCR_DME 3
+#define BM_UART_UFCR_DME 0x8
+#define BF_UART_UFCR_DME(v) (((v) & 0x1) << 3)
+#define BFM_UART_UFCR_DME(v) BM_UART_UFCR_DME
+#define BF_UART_UFCR_DME_V(e) BF_UART_UFCR_DME(BV_UART_UFCR_DME__##e)
+#define BFM_UART_UFCR_DME_V(v) BM_UART_UFCR_DME
+#define BP_UART_UFCR_TFRT 2
+#define BM_UART_UFCR_TFRT 0x4
+#define BF_UART_UFCR_TFRT(v) (((v) & 0x1) << 2)
+#define BFM_UART_UFCR_TFRT(v) BM_UART_UFCR_TFRT
+#define BF_UART_UFCR_TFRT_V(e) BF_UART_UFCR_TFRT(BV_UART_UFCR_TFRT__##e)
+#define BFM_UART_UFCR_TFRT_V(v) BM_UART_UFCR_TFRT
+#define BP_UART_UFCR_RFRT 1
+#define BM_UART_UFCR_RFRT 0x2
+#define BF_UART_UFCR_RFRT(v) (((v) & 0x1) << 1)
+#define BFM_UART_UFCR_RFRT(v) BM_UART_UFCR_RFRT
+#define BF_UART_UFCR_RFRT_V(e) BF_UART_UFCR_RFRT(BV_UART_UFCR_RFRT__##e)
+#define BFM_UART_UFCR_RFRT_V(v) BM_UART_UFCR_RFRT
+#define BP_UART_UFCR_FME 0
+#define BM_UART_UFCR_FME 0x1
+#define BF_UART_UFCR_FME(v) (((v) & 0x1) << 0)
+#define BFM_UART_UFCR_FME(v) BM_UART_UFCR_FME
+#define BF_UART_UFCR_FME_V(e) BF_UART_UFCR_FME(BV_UART_UFCR_FME__##e)
+#define BFM_UART_UFCR_FME_V(v) BM_UART_UFCR_FME
+
+#define REG_UART_ULCR(_n1) jz_reg(UART_ULCR(_n1))
+#define JA_UART_ULCR(_n1) (0xb0030000 + (_n1) * 0x1000 + 0xc)
+#define JT_UART_ULCR(_n1) JIO_32_RW
+#define JN_UART_ULCR(_n1) UART_ULCR
+#define JI_UART_ULCR(_n1) (_n1)
+#define BP_UART_ULCR_WLS 0
+#define BM_UART_ULCR_WLS 0x3
+#define BV_UART_ULCR_WLS__5BITS 0x0
+#define BV_UART_ULCR_WLS__6BITS 0x1
+#define BV_UART_ULCR_WLS__7BITS 0x2
+#define BV_UART_ULCR_WLS__8BITS 0x3
+#define BF_UART_ULCR_WLS(v) (((v) & 0x3) << 0)
+#define BFM_UART_ULCR_WLS(v) BM_UART_ULCR_WLS
+#define BF_UART_ULCR_WLS_V(e) BF_UART_ULCR_WLS(BV_UART_ULCR_WLS__##e)
+#define BFM_UART_ULCR_WLS_V(v) BM_UART_ULCR_WLS
+#define BP_UART_ULCR_DLAB 7
+#define BM_UART_ULCR_DLAB 0x80
+#define BF_UART_ULCR_DLAB(v) (((v) & 0x1) << 7)
+#define BFM_UART_ULCR_DLAB(v) BM_UART_ULCR_DLAB
+#define BF_UART_ULCR_DLAB_V(e) BF_UART_ULCR_DLAB(BV_UART_ULCR_DLAB__##e)
+#define BFM_UART_ULCR_DLAB_V(v) BM_UART_ULCR_DLAB
+#define BP_UART_ULCR_SBK 6
+#define BM_UART_ULCR_SBK 0x40
+#define BF_UART_ULCR_SBK(v) (((v) & 0x1) << 6)
+#define BFM_UART_ULCR_SBK(v) BM_UART_ULCR_SBK
+#define BF_UART_ULCR_SBK_V(e) BF_UART_ULCR_SBK(BV_UART_ULCR_SBK__##e)
+#define BFM_UART_ULCR_SBK_V(v) BM_UART_ULCR_SBK
+#define BP_UART_ULCR_STPAR 5
+#define BM_UART_ULCR_STPAR 0x20
+#define BF_UART_ULCR_STPAR(v) (((v) & 0x1) << 5)
+#define BFM_UART_ULCR_STPAR(v) BM_UART_ULCR_STPAR
+#define BF_UART_ULCR_STPAR_V(e) BF_UART_ULCR_STPAR(BV_UART_ULCR_STPAR__##e)
+#define BFM_UART_ULCR_STPAR_V(v) BM_UART_ULCR_STPAR
+#define BP_UART_ULCR_PARM 4
+#define BM_UART_ULCR_PARM 0x10
+#define BV_UART_ULCR_PARM__ODD 0x0
+#define BV_UART_ULCR_PARM__EVEN 0x1
+#define BF_UART_ULCR_PARM(v) (((v) & 0x1) << 4)
+#define BFM_UART_ULCR_PARM(v) BM_UART_ULCR_PARM
+#define BF_UART_ULCR_PARM_V(e) BF_UART_ULCR_PARM(BV_UART_ULCR_PARM__##e)
+#define BFM_UART_ULCR_PARM_V(v) BM_UART_ULCR_PARM
+#define BP_UART_ULCR_PARE 3
+#define BM_UART_ULCR_PARE 0x8
+#define BF_UART_ULCR_PARE(v) (((v) & 0x1) << 3)
+#define BFM_UART_ULCR_PARE(v) BM_UART_ULCR_PARE
+#define BF_UART_ULCR_PARE_V(e) BF_UART_ULCR_PARE(BV_UART_ULCR_PARE__##e)
+#define BFM_UART_ULCR_PARE_V(v) BM_UART_ULCR_PARE
+#define BP_UART_ULCR_SBLS 2
+#define BM_UART_ULCR_SBLS 0x4
+#define BV_UART_ULCR_SBLS__1_STOP_BIT 0x0
+#define BV_UART_ULCR_SBLS__2_STOP_BITS 0x1
+#define BF_UART_ULCR_SBLS(v) (((v) & 0x1) << 2)
+#define BFM_UART_ULCR_SBLS(v) BM_UART_ULCR_SBLS
+#define BF_UART_ULCR_SBLS_V(e) BF_UART_ULCR_SBLS(BV_UART_ULCR_SBLS__##e)
+#define BFM_UART_ULCR_SBLS_V(v) BM_UART_ULCR_SBLS
+
+#define REG_UART_UMCR(_n1) jz_reg(UART_UMCR(_n1))
+#define JA_UART_UMCR(_n1) (0xb0030000 + (_n1) * 0x1000 + 0x10)
+#define JT_UART_UMCR(_n1) JIO_32_RW
+#define JN_UART_UMCR(_n1) UART_UMCR
+#define JI_UART_UMCR(_n1) (_n1)
+#define BP_UART_UMCR_MDCE 7
+#define BM_UART_UMCR_MDCE 0x80
+#define BF_UART_UMCR_MDCE(v) (((v) & 0x1) << 7)
+#define BFM_UART_UMCR_MDCE(v) BM_UART_UMCR_MDCE
+#define BF_UART_UMCR_MDCE_V(e) BF_UART_UMCR_MDCE(BV_UART_UMCR_MDCE__##e)
+#define BFM_UART_UMCR_MDCE_V(v) BM_UART_UMCR_MDCE
+#define BP_UART_UMCR_FCM 6
+#define BM_UART_UMCR_FCM 0x40
+#define BF_UART_UMCR_FCM(v) (((v) & 0x1) << 6)
+#define BFM_UART_UMCR_FCM(v) BM_UART_UMCR_FCM
+#define BF_UART_UMCR_FCM_V(e) BF_UART_UMCR_FCM(BV_UART_UMCR_FCM__##e)
+#define BFM_UART_UMCR_FCM_V(v) BM_UART_UMCR_FCM
+#define BP_UART_UMCR_LOOP 4
+#define BM_UART_UMCR_LOOP 0x10
+#define BF_UART_UMCR_LOOP(v) (((v) & 0x1) << 4)
+#define BFM_UART_UMCR_LOOP(v) BM_UART_UMCR_LOOP
+#define BF_UART_UMCR_LOOP_V(e) BF_UART_UMCR_LOOP(BV_UART_UMCR_LOOP__##e)
+#define BFM_UART_UMCR_LOOP_V(v) BM_UART_UMCR_LOOP
+#define BP_UART_UMCR_RTS 1
+#define BM_UART_UMCR_RTS 0x2
+#define BF_UART_UMCR_RTS(v) (((v) & 0x1) << 1)
+#define BFM_UART_UMCR_RTS(v) BM_UART_UMCR_RTS
+#define BF_UART_UMCR_RTS_V(e) BF_UART_UMCR_RTS(BV_UART_UMCR_RTS__##e)
+#define BFM_UART_UMCR_RTS_V(v) BM_UART_UMCR_RTS
+
+#define REG_UART_ULSR(_n1) jz_reg(UART_ULSR(_n1))
+#define JA_UART_ULSR(_n1) (0xb0030000 + (_n1) * 0x1000 + 0x14)
+#define JT_UART_ULSR(_n1) JIO_32_RW
+#define JN_UART_ULSR(_n1) UART_ULSR
+#define JI_UART_ULSR(_n1) (_n1)
+#define BP_UART_ULSR_FIFOE 7
+#define BM_UART_ULSR_FIFOE 0x80
+#define BF_UART_ULSR_FIFOE(v) (((v) & 0x1) << 7)
+#define BFM_UART_ULSR_FIFOE(v) BM_UART_ULSR_FIFOE
+#define BF_UART_ULSR_FIFOE_V(e) BF_UART_ULSR_FIFOE(BV_UART_ULSR_FIFOE__##e)
+#define BFM_UART_ULSR_FIFOE_V(v) BM_UART_ULSR_FIFOE
+#define BP_UART_ULSR_TEMP 6
+#define BM_UART_ULSR_TEMP 0x40
+#define BF_UART_ULSR_TEMP(v) (((v) & 0x1) << 6)
+#define BFM_UART_ULSR_TEMP(v) BM_UART_ULSR_TEMP
+#define BF_UART_ULSR_TEMP_V(e) BF_UART_ULSR_TEMP(BV_UART_ULSR_TEMP__##e)
+#define BFM_UART_ULSR_TEMP_V(v) BM_UART_ULSR_TEMP
+#define BP_UART_ULSR_TDRQ 5
+#define BM_UART_ULSR_TDRQ 0x20
+#define BF_UART_ULSR_TDRQ(v) (((v) & 0x1) << 5)
+#define BFM_UART_ULSR_TDRQ(v) BM_UART_ULSR_TDRQ
+#define BF_UART_ULSR_TDRQ_V(e) BF_UART_ULSR_TDRQ(BV_UART_ULSR_TDRQ__##e)
+#define BFM_UART_ULSR_TDRQ_V(v) BM_UART_ULSR_TDRQ
+#define BP_UART_ULSR_BI 4
+#define BM_UART_ULSR_BI 0x10
+#define BF_UART_ULSR_BI(v) (((v) & 0x1) << 4)
+#define BFM_UART_ULSR_BI(v) BM_UART_ULSR_BI
+#define BF_UART_ULSR_BI_V(e) BF_UART_ULSR_BI(BV_UART_ULSR_BI__##e)
+#define BFM_UART_ULSR_BI_V(v) BM_UART_ULSR_BI
+#define BP_UART_ULSR_FMER 3
+#define BM_UART_ULSR_FMER 0x8
+#define BF_UART_ULSR_FMER(v) (((v) & 0x1) << 3)
+#define BFM_UART_ULSR_FMER(v) BM_UART_ULSR_FMER
+#define BF_UART_ULSR_FMER_V(e) BF_UART_ULSR_FMER(BV_UART_ULSR_FMER__##e)
+#define BFM_UART_ULSR_FMER_V(v) BM_UART_ULSR_FMER
+#define BP_UART_ULSR_PARER 2
+#define BM_UART_ULSR_PARER 0x4
+#define BF_UART_ULSR_PARER(v) (((v) & 0x1) << 2)
+#define BFM_UART_ULSR_PARER(v) BM_UART_ULSR_PARER
+#define BF_UART_ULSR_PARER_V(e) BF_UART_ULSR_PARER(BV_UART_ULSR_PARER__##e)
+#define BFM_UART_ULSR_PARER_V(v) BM_UART_ULSR_PARER
+#define BP_UART_ULSR_OVER 1
+#define BM_UART_ULSR_OVER 0x2
+#define BF_UART_ULSR_OVER(v) (((v) & 0x1) << 1)
+#define BFM_UART_ULSR_OVER(v) BM_UART_ULSR_OVER
+#define BF_UART_ULSR_OVER_V(e) BF_UART_ULSR_OVER(BV_UART_ULSR_OVER__##e)
+#define BFM_UART_ULSR_OVER_V(v) BM_UART_ULSR_OVER
+#define BP_UART_ULSR_DRY 0
+#define BM_UART_ULSR_DRY 0x1
+#define BF_UART_ULSR_DRY(v) (((v) & 0x1) << 0)
+#define BFM_UART_ULSR_DRY(v) BM_UART_ULSR_DRY
+#define BF_UART_ULSR_DRY_V(e) BF_UART_ULSR_DRY(BV_UART_ULSR_DRY__##e)
+#define BFM_UART_ULSR_DRY_V(v) BM_UART_ULSR_DRY
+
+#define REG_UART_UMSR(_n1) jz_reg(UART_UMSR(_n1))
+#define JA_UART_UMSR(_n1) (0xb0030000 + (_n1) * 0x1000 + 0x18)
+#define JT_UART_UMSR(_n1) JIO_32_RW
+#define JN_UART_UMSR(_n1) UART_UMSR
+#define JI_UART_UMSR(_n1) (_n1)
+#define BP_UART_UMSR_CTS 4
+#define BM_UART_UMSR_CTS 0x10
+#define BF_UART_UMSR_CTS(v) (((v) & 0x1) << 4)
+#define BFM_UART_UMSR_CTS(v) BM_UART_UMSR_CTS
+#define BF_UART_UMSR_CTS_V(e) BF_UART_UMSR_CTS(BV_UART_UMSR_CTS__##e)
+#define BFM_UART_UMSR_CTS_V(v) BM_UART_UMSR_CTS
+#define BP_UART_UMSR_CCTS 0
+#define BM_UART_UMSR_CCTS 0x1
+#define BF_UART_UMSR_CCTS(v) (((v) & 0x1) << 0)
+#define BFM_UART_UMSR_CCTS(v) BM_UART_UMSR_CCTS
+#define BF_UART_UMSR_CCTS_V(e) BF_UART_UMSR_CCTS(BV_UART_UMSR_CCTS__##e)
+#define BFM_UART_UMSR_CCTS_V(v) BM_UART_UMSR_CCTS
+
+#define REG_UART_USPR(_n1) jz_reg(UART_USPR(_n1))
+#define JA_UART_USPR(_n1) (0xb0030000 + (_n1) * 0x1000 + 0x1c)
+#define JT_UART_USPR(_n1) JIO_32_RW
+#define JN_UART_USPR(_n1) UART_USPR
+#define JI_UART_USPR(_n1) (_n1)
+
+#define REG_UART_ISR(_n1) jz_reg(UART_ISR(_n1))
+#define JA_UART_ISR(_n1) (0xb0030000 + (_n1) * 0x1000 + 0x20)
+#define JT_UART_ISR(_n1) JIO_32_RW
+#define JN_UART_ISR(_n1) UART_ISR
+#define JI_UART_ISR(_n1) (_n1)
+#define BP_UART_ISR_RDPL 4
+#define BM_UART_ISR_RDPL 0x10
+#define BF_UART_ISR_RDPL(v) (((v) & 0x1) << 4)
+#define BFM_UART_ISR_RDPL(v) BM_UART_ISR_RDPL
+#define BF_UART_ISR_RDPL_V(e) BF_UART_ISR_RDPL(BV_UART_ISR_RDPL__##e)
+#define BFM_UART_ISR_RDPL_V(v) BM_UART_ISR_RDPL
+#define BP_UART_ISR_TDPL 3
+#define BM_UART_ISR_TDPL 0x8
+#define BF_UART_ISR_TDPL(v) (((v) & 0x1) << 3)
+#define BFM_UART_ISR_TDPL(v) BM_UART_ISR_TDPL
+#define BF_UART_ISR_TDPL_V(e) BF_UART_ISR_TDPL(BV_UART_ISR_TDPL__##e)
+#define BFM_UART_ISR_TDPL_V(v) BM_UART_ISR_TDPL
+#define BP_UART_ISR_XMODE 2
+#define BM_UART_ISR_XMODE 0x4
+#define BF_UART_ISR_XMODE(v) (((v) & 0x1) << 2)
+#define BFM_UART_ISR_XMODE(v) BM_UART_ISR_XMODE
+#define BF_UART_ISR_XMODE_V(e) BF_UART_ISR_XMODE(BV_UART_ISR_XMODE__##e)
+#define BFM_UART_ISR_XMODE_V(v) BM_UART_ISR_XMODE
+#define BP_UART_ISR_RCVEIR 1
+#define BM_UART_ISR_RCVEIR 0x2
+#define BF_UART_ISR_RCVEIR(v) (((v) & 0x1) << 1)
+#define BFM_UART_ISR_RCVEIR(v) BM_UART_ISR_RCVEIR
+#define BF_UART_ISR_RCVEIR_V(e) BF_UART_ISR_RCVEIR(BV_UART_ISR_RCVEIR__##e)
+#define BFM_UART_ISR_RCVEIR_V(v) BM_UART_ISR_RCVEIR
+#define BP_UART_ISR_XMITIR 0
+#define BM_UART_ISR_XMITIR 0x1
+#define BF_UART_ISR_XMITIR(v) (((v) & 0x1) << 0)
+#define BFM_UART_ISR_XMITIR(v) BM_UART_ISR_XMITIR
+#define BF_UART_ISR_XMITIR_V(e) BF_UART_ISR_XMITIR(BV_UART_ISR_XMITIR__##e)
+#define BFM_UART_ISR_XMITIR_V(v) BM_UART_ISR_XMITIR
+
+#define REG_UART_UMR(_n1) jz_reg(UART_UMR(_n1))
+#define JA_UART_UMR(_n1) (0xb0030000 + (_n1) * 0x1000 + 0x24)
+#define JT_UART_UMR(_n1) JIO_32_RW
+#define JN_UART_UMR(_n1) UART_UMR
+#define JI_UART_UMR(_n1) (_n1)
+
+#define REG_UART_UACR(_n1) jz_reg(UART_UACR(_n1))
+#define JA_UART_UACR(_n1) (0xb0030000 + (_n1) * 0x1000 + 0x28)
+#define JT_UART_UACR(_n1) JIO_32_RW
+#define JN_UART_UACR(_n1) UART_UACR
+#define JI_UART_UACR(_n1) (_n1)
+
+#define REG_UART_URCR(_n1) jz_reg(UART_URCR(_n1))
+#define JA_UART_URCR(_n1) (0xb0030000 + (_n1) * 0x1000 + 0x40)
+#define JT_UART_URCR(_n1) JIO_32_RW
+#define JN_UART_URCR(_n1) UART_URCR
+#define JI_UART_URCR(_n1) (_n1)
+
+#define REG_UART_UTCR(_n1) jz_reg(UART_UTCR(_n1))
+#define JA_UART_UTCR(_n1) (0xb0030000 + (_n1) * 0x1000 + 0x44)
+#define JT_UART_UTCR(_n1) JIO_32_RW
+#define JN_UART_UTCR(_n1) UART_UTCR
+#define JI_UART_UTCR(_n1) (_n1)
+
+#endif /* __HEADERGEN_UART_H__*/
diff --git a/firmware/target/mips/ingenic_x1000/x1000boot.make b/firmware/target/mips/ingenic_x1000/x1000boot.make
new file mode 100644
index 0000000000..0bdf5cf7b4
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/x1000boot.make
@@ -0,0 +1,94 @@
+# __________ __ ___.
+# Open \______ \ ____ ____ | | _\_ |__ _______ ___
+# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+# \/ \/ \/ \/ \/
+# $Id$
+#
+
+include $(ROOTDIR)/lib/microtar/microtar.make
+
+INCLUDES += -I$(APPSDIR)
+SRC += $(call preprocess, $(APPSDIR)/SOURCES)
+
+LDSDEP := $(FIRMDIR)/export/cpu.h $(FIRMDIR)/export/config/$(MODELNAME).h
+
+BOOTLDS := $(FIRMDIR)/target/$(CPU)/$(MANUFACTURER)/boot.lds
+BOOTLINK := $(BUILDDIR)/boot.link
+BOOTEXT := $(suffix $(BINARY))
+
+SPLLDS := $(FIRMDIR)/target/$(CPU)/$(MANUFACTURER)/spl.lds
+SPLLINK := $(BUILDDIR)/spl.link
+SPLBINARY := spl$(BOOTEXT)
+
+BLINFO = $(BUILDDIR)/bootloader-info.txt
+
+CLEANOBJS += $(BUILDDIR)/bootloader.* $(BUILDDIR)/spl.*
+
+# Currently not needed
+#include $(FIRMDIR)/target/$(CPU)/$(MANUFACTURER)/$(MODELNAME)/boot.make
+
+.SECONDEXPANSION:
+
+### Bootloader
+
+$(BOOTLINK): $(BOOTLDS) $(LDSDEP)
+ $(call PRINTS,PP $(@F))
+ $(call preprocess2file,$<,$@,)
+
+$(BUILDDIR)/bootloader.elf: $$(OBJ) $(FIRMLIB) $(CORE_LIBS) $$(BOOTLINK)
+ $(call PRINTS,LD $(@F))$(CC) $(GCCOPTS) -Os -nostdlib -o $@ $(OBJ) \
+ -L$(BUILDDIR)/firmware -lfirmware \
+ -L$(BUILDDIR)/lib $(call a2lnk, $(CORE_LIBS)) \
+ -lgcc -T$(BOOTLINK) $(GLOBAL_LDOPTS) \
+ -Wl,--gc-sections -Wl,-Map,$(BUILDDIR)/bootloader.map
+
+$(BUILDDIR)/bootloader.bin: $(BUILDDIR)/bootloader.elf
+ $(call PRINTS,OC $(@F))$(call objcopy,$<,$@)
+
+$(BUILDDIR)/bootloader.ucl: $(BUILDDIR)/bootloader.bin
+ $(call PRINTS,UCLPACK $(@F))$(TOOLSDIR)/uclpack --nrv2e -9 $< $@ >/dev/null
+
+
+### SPL
+
+$(SPLLINK): $(SPLLDS) $(LDSDEP)
+ $(call PRINTS,PP $(@F))
+ $(call preprocess2file,$<,$@,)
+
+$(BUILDDIR)/spl.elf: $$(OBJ) $(FIRMLIB) $(CORE_LIBS) $$(SPLLINK)
+ $(call PRINTS,LD $(@F))$(CC) $(GCCOPTS) -Os -nostdlib -o $@ $(OBJ) \
+ -L$(BUILDDIR)/firmware -lfirmware \
+ -L$(BUILDDIR)/lib $(call a2lnk, $(CORE_LIBS)) \
+ -lgcc -T$(SPLLINK) $(GLOBAL_LDOPTS) \
+ -Wl,--gc-sections -Wl,-Map,$(BUILDDIR)/spl.map
+
+$(BUILDDIR)/spl.bin: $(BUILDDIR)/spl.elf
+ $(call PRINTS,OC $(@F))$(call objcopy,$<,$@)
+
+$(BUILDDIR)/$(SPLBINARY): $(BUILDDIR)/spl.bin
+ $(call PRINTS,MKSPL $(@F))$(MKFIRMWARE) $< $@
+
+
+### Generating the update package
+
+# suppress regenerating bootloader-info if nothing has changed
+BLVERSION:=$(SVNVERSION)
+OLDBLVERSION:=$(shell head -n1 $(BLINFO) 2>/dev/null || echo "NOREVISION")
+
+ifneq ($(BLVERSION),$(OLDBLVERSION))
+.PHONY: $(BLINFO)
+endif
+
+$(BLINFO):
+ $(call PRINTS,GEN $(@F))echo $(SVNVERSION) > $@
+
+# The "binary" is actually an update package which is just a tar archive
+$(BUILDDIR)/$(BINARY): $(BUILDDIR)/$(SPLBINARY) \
+ $(BUILDDIR)/bootloader.ucl \
+ $(BLINFO)
+ $(call PRINTS,TAR $(@F))tar -C $(BUILDDIR) \
+ --numeric-owner --no-acls --no-xattrs --no-selinux \
+ --mode=0644 --owner=0 --group=0 \
+ -cf $@ $(call full_path_subst,$(BUILDDIR)/%,%,$^)
diff --git a/firmware/target/mips/mipsr2-endian.h b/firmware/target/mips/mipsr2-endian.h
index 08992bfcdd..4de1eb825b 100644
--- a/firmware/target/mips/mipsr2-endian.h
+++ b/firmware/target/mips/mipsr2-endian.h
@@ -41,4 +41,11 @@ static inline uint16_t swap16_hw(uint16_t value)
return (uint16_t)out;
}
+static inline uint32_t swap_odd_even32_hw(uint32_t value)
+{
+ register uint32_t out;
+ __asm__ ("wsbh %0, %1" : "=r"(out) : "r"(value));
+ return out;
+}
+
#endif /* __MIPSR2_ENDIAN_H__ */
diff --git a/firmware/target/mips/mmu-mips.h b/firmware/target/mips/mmu-mips.h
index b54807f060..ca865f9909 100644
--- a/firmware/target/mips/mmu-mips.h
+++ b/firmware/target/mips/mmu-mips.h
@@ -28,33 +28,36 @@
* called safely eg. by the bootloader or RoLo, which need to flush the
* cache before jumping to the loaded binary.
*/
-#ifndef MIPS_CACHEFUNC_ATTR
-# define MIPS_CACHEFUNC_ATTR __attribute__((section(".icode")))
-#endif
+#define MIPS_CACHEFUNC_API(ret, name, args) \
+ ret name args __attribute__((section( ".icode." #name )))
void map_address(unsigned long virtual, unsigned long physical,
unsigned long length, unsigned int cache_flags);
void mmu_init(void);
/* Commits entire DCache */
-void commit_dcache(void) MIPS_CACHEFUNC_ATTR;
+#if 0 /* NOTE: This is currently aliased to commit_discard_dcache. Causes compilation errors with newer GCC if we try to assign it to a section here */
+//MIPS_CACHEFUNC_API(void, commit_dcache, (void));
+#else
+void commit_dcache(void);
+#endif
/* Commit and discard entire DCache, will do writeback */
-void commit_discard_dcache(void) MIPS_CACHEFUNC_ATTR;
+MIPS_CACHEFUNC_API(void, commit_discard_dcache, (void));
/* Write DCache back to RAM for the given range and remove cache lines
* from DCache afterwards */
-void commit_discard_dcache_range(const void *base, unsigned int size) MIPS_CACHEFUNC_ATTR;
+MIPS_CACHEFUNC_API(void, commit_discard_dcache_range, (const void *base, unsigned int size));
/* Write DCache back to RAM for the given range */
-void commit_dcache_range(const void *base, unsigned int size) MIPS_CACHEFUNC_ATTR;
+MIPS_CACHEFUNC_API(void, commit_dcache_range, (const void *base, unsigned int size));
/*
* Remove cache lines for the given range from DCache
* will *NOT* do write back except for buffer edges not on a line boundary
*/
-void discard_dcache_range(const void *base, unsigned int size) MIPS_CACHEFUNC_ATTR;
+MIPS_CACHEFUNC_API(void, discard_dcache_range, (const void *base, unsigned int size));
/* Discards the entire ICache, and commit+discards the entire DCache */
-void commit_discard_idcache(void) MIPS_CACHEFUNC_ATTR;
+MIPS_CACHEFUNC_API(void, commit_discard_idcache, (void));
#endif /* __MMU_MIPS_INCLUDE_H */
diff --git a/firmware/target/mips/system-mips.c b/firmware/target/mips/system-mips.c
new file mode 100644
index 0000000000..e6bb2d8ea6
--- /dev/null
+++ b/firmware/target/mips/system-mips.c
@@ -0,0 +1,175 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2022 Aidan MacDonald
+ *
+ * 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 "backtrace.h"
+#include "lcd.h"
+#include "backlight-target.h"
+#include "font.h"
+#include "logf.h"
+#include "mips.h"
+#undef sp /* breaks backtrace lib */
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+
+/* copies exception frame info and error pc to the backtrace context */
+static void setup_exception_bt(struct mips_exception_frame* frame,
+ unsigned long epc, struct mips_bt_context* ctx)
+{
+ ctx->pc = (void*)epc;
+ ctx->sp = (void*)frame->gpr[29 - 3];
+ ctx->depth = 0;
+ ctx->valid = (1 << MIPSBT_RA);
+ ctx->reg[MIPSBT_RA] = frame->gpr[31 - 3];
+}
+
+/* dump backtrace for an exception */
+static void exception_bt(void* frame, unsigned long epc, unsigned* line)
+{
+ struct mips_bt_context ctx;
+ setup_exception_bt(frame, epc, &ctx);
+ rb_backtrace_ctx(&ctx, line);
+}
+
+/*
+ * TODO: This should be converted into a generic panic routine that accepts
+ * a backtrace context argument but the ARM backtrace setup will need to be
+ * refactored in order to do that
+ */
+static void exception_dump(void* frame, unsigned long epc,
+ const char* fmt, ...)
+{
+ extern char panic_buf[128];
+ va_list ap;
+
+ set_irq_level(DISABLE_INTERRUPTS);
+
+ va_start(ap, fmt);
+ vsnprintf(panic_buf, sizeof(panic_buf), fmt, ap);
+ va_end(ap);
+
+ lcd_set_viewport(NULL);
+#if LCD_DEPTH > 1
+ lcd_set_backdrop(NULL);
+ lcd_set_drawmode(DRMODE_SOLID);
+ lcd_set_foreground(LCD_BLACK);
+ lcd_set_background(LCD_WHITE);
+#endif
+
+ lcd_clear_display();
+ lcd_setfont(FONT_SYSFIXED);
+
+ unsigned y = 1;
+ lcd_puts(1, y++, "*EXCEPTION*");
+
+ /* wrap panic message */
+ {
+ const int linechars = (LCD_WIDTH / SYSFONT_WIDTH) - 2;
+
+ int pos = 0, len = strlen(panic_buf);
+ while(len > 0) {
+ int idx = pos + MIN(len, linechars);
+ char c = panic_buf[idx];
+ panic_buf[idx] = 0;
+ lcd_puts(1, y++, &panic_buf[pos]);
+ panic_buf[idx] = c;
+
+ len -= linechars;
+ pos += linechars;
+ }
+ }
+
+ exception_bt(frame, epc, &y);
+#ifdef ROCKBOX_HAS_LOGF
+ logf_panic_dump(&y);
+#endif
+
+ lcd_update();
+ backlight_hw_on();
+
+#ifdef HAVE_ADJUSTABLE_CPU_FREQ
+ if (cpu_boost_lock())
+ {
+ set_cpu_frequency(0);
+ cpu_boost_unlock();
+ }
+#endif
+
+#ifdef HAVE_ATA_POWER_OFF
+ ide_power_enable(false);
+#endif
+
+ system_exception_wait();
+ system_reboot();
+ while(1);
+}
+
+#define EXC(x,y) case (x): return (y);
+static char* parse_exception(unsigned long cause)
+{
+ switch(cause & M_CauseExcCode)
+ {
+ EXC(EXC_INT, "Interrupt");
+ EXC(EXC_MOD, "TLB Modified");
+ EXC(EXC_TLBL, "TLB Exception (Load or Ifetch)");
+ EXC(EXC_ADEL, "Address Error (Load or Ifetch)");
+ EXC(EXC_ADES, "Address Error (Store)");
+ EXC(EXC_TLBS, "TLB Exception (Store)");
+ EXC(EXC_IBE, "Instruction Bus Error");
+ EXC(EXC_DBE, "Data Bus Error");
+ EXC(EXC_SYS, "Syscall");
+ EXC(EXC_BP, "Breakpoint");
+ EXC(EXC_RI, "Reserved Instruction");
+ EXC(EXC_CPU, "Coprocessor Unusable");
+ EXC(EXC_OV, "Overflow");
+ EXC(EXC_TR, "Trap Instruction");
+ EXC(EXC_FPE, "Floating Point Exception");
+ EXC(EXC_C2E, "COP2 Exception");
+ EXC(EXC_MDMX, "MDMX Exception");
+ EXC(EXC_WATCH, "Watch Exception");
+ EXC(EXC_MCHECK, "Machine Check Exception");
+ EXC(EXC_CacheErr, "Cache error caused re-entry to Debug Mode");
+ default:
+ return 0;
+ }
+}
+#undef EXC
+
+void exception_handler(void* frame, unsigned long epc)
+{
+ unsigned long cause = read_c0_cause();
+
+ exception_dump(frame, epc, "%s [0x%08x] at %08lx",
+ parse_exception(cause), read_c0_badvaddr(), epc);
+}
+
+void cache_error_handler(void* frame, unsigned long epc)
+{
+ exception_dump(frame, epc, "Cache Error [0x%08x] at %08lx",
+ read_c0_cacheerr(), epc);
+}
+
+void tlb_refill_handler(void* frame, unsigned long epc)
+{
+ exception_dump(frame, epc, "TLB refill at %08lx [0x%x]",
+ epc, read_c0_badvaddr());
+}
diff --git a/firmware/target/arm/imx233/samsung-ypz5/ftl-target.h b/firmware/target/mips/system-mips.h
index cb943e34f2..d9108ef7c2 100644
--- a/firmware/target/arm/imx233/samsung-ypz5/ftl-target.h
+++ b/firmware/target/mips/system-mips.h
@@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Copyright (C) 2009 by Michael Sparmann
+ * Copyright (C) 2022 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -19,21 +19,22 @@
*
****************************************************************************/
-#ifndef __FTL_TARGET_H__
-#define __FTL_TARGET_H__
+#ifndef SYSTEM_MIPS_H
+#define SYSTEM_MIPS_H
-#include "config.h"
-#include "inttypes.h"
+#include <stdint.h>
-#ifdef BOOTLOADER
-/* Bootloaders don't need write access */
-#define FTL_READONLY
-#endif
+struct mips_exception_frame {
+ uint32_t gpr[29]; /* GPRs $1-$25, $28-$31 */
+ uint32_t lo;
+ uint32_t hi;
+ uint32_t c0_status;
+ uint32_t c0_epc;
+};
-uint32_t ftl_init(void);
-uint32_t ftl_read(uint32_t sector, uint32_t count, void* buffer);
-uint32_t ftl_write(uint32_t sector, uint32_t count, const void* buffer);
-uint32_t ftl_sync(void);
+void intr_handler(void);
+void exception_handler(void* frame, unsigned long epc);
+void cache_error_handler(void* frame, unsigned long epc);
+void tlb_refill_handler(void* frame, unsigned long epc);
-
-#endif
+#endif /* SYSTEM_MIPS_H */
diff --git a/firmware/usb.c b/firmware/usb.c
index 05597dff9b..32f4902c7c 100644
--- a/firmware/usb.c
+++ b/firmware/usb.c
@@ -57,7 +57,7 @@
(defined(HAVE_USBSTACK) && defined(IPOD_NANO2G)) || \
(defined(HAVE_USBSTACK) && (defined(CREATIVE_ZVx))) || \
(defined(HAVE_USBSTACK) && (defined(OLYMPUS_MROBE_500))) || \
- defined(CPU_TCC77X) || defined(CPU_TCC780X) || \
+ defined(CPU_TCC780X) || \
(CONFIG_USBOTG == USBOTG_JZ4740) || \
(CONFIG_USBOTG == USBOTG_JZ4760)
/* TODO: condition should be reset to be only the original
@@ -65,6 +65,9 @@
#define USB_FULL_INIT
#endif
+/* USB detect debouncing interval (200ms taken from the usb polling code) */
+#define USB_DEBOUNCE_TIME (200*HZ/1000)
+
bool do_screendump_instead_of_usb = false;
#if !defined(SIMULATOR) && !defined(USB_NONE)
@@ -476,7 +479,12 @@ static void NORETURN_ATTR usb_thread(void)
usb_state = USB_POWERED;
usb_stack_enable(true);
-
+#ifndef BOOTLOADER
+#ifndef HAVE_USB_POWER
+ int usb_mode = -1;
+#endif
+ send_event(SYS_EVENT_USB_INSERTED, &usb_mode);
+#endif
/* Power (charging-only) button */
#ifdef HAVE_USB_POWER
new_usbmode = usb_mode;
@@ -486,37 +494,12 @@ static void NORETURN_ATTR usb_thread(void)
if (button_status() & ~USBPOWER_BTN_IGNORE)
new_usbmode = USB_MODE_MASS_STORAGE;
break;
-#ifndef BOOTLOADER
- case USB_MODE_ASK:
- new_usbmode = USB_MODE_ASK;
- break;
-#endif
default:
case USB_MODE_MASS_STORAGE:
if (button_status() & ~USBPOWER_BTN_IGNORE)
new_usbmode = USB_MODE_CHARGE;
break;
}
-
-#ifndef BOOTLOADER
- if (new_usbmode == USB_MODE_ASK)
- {
- push_current_activity(ACTIVITY_USBSCREEN);
- if (yesno_pop(ID2P(LANG_ENTER_USB_STORAGE_MODE_QUERY)))
- new_usbmode = USB_MODE_MASS_STORAGE;
- else
- new_usbmode = USB_MODE_CHARGE;
- pop_current_activity();
- /* Force full redraw */
-// queue_post(&button_queue, BUTTON_REDRAW, 0);
-// Alternative approach, as above is supposedly inadequate by design.
- FOR_NB_SCREENS(i)
- {
- struct screen *screen = &screens[i];
- screen->set_viewport(NULL);
- }
- }
-#endif
#endif
#ifndef USB_DETECT_BY_REQUEST
@@ -547,7 +530,9 @@ static void NORETURN_ATTR usb_thread(void)
#ifdef HAVE_USB_POWER
new_usbmode = usb_mode;
#endif
-
+#ifndef BOOTLOADER
+ send_event(SYS_EVENT_USB_EXTRACTED, NULL);
+#endif
usb_set_host_present(false);
break;
/* USB_EXTRACTED: */
@@ -598,8 +583,33 @@ void usb_charger_update(void)
#endif
#ifdef USB_STATUS_BY_EVENT
+static int usb_status_tmo_callback(struct timeout* tmo)
+{
+ if(usb_monitor_enabled)
+ {
+ int current_status = usb_detect();
+ int* last_status = (int*)tmo->data;
+
+ if(current_status != *last_status)
+ {
+ /* Signal changed during the timeout; wait longer */
+ *last_status = current_status;
+ return USB_DEBOUNCE_TIME;
+ }
+
+ /* Signal is stable, post the event. The thread will deal with
+ * any spurious transitions (like inserted -> inserted). */
+ queue_post(&usb_queue, current_status, 0);
+ }
+
+ return 0;
+}
+
void usb_status_event(int current_status)
{
+ static struct timeout tmo;
+ static int last_status = USB_EXTRACTED;
+
/* Caller isn't expected to filter for changes in status.
* current_status:
* USB_INSERTED, USB_EXTRACTED
@@ -607,8 +617,9 @@ void usb_status_event(int current_status)
if(usb_monitor_enabled)
{
int oldstatus = disable_irq_save(); /* Dual-use function */
- queue_remove_from_head(&usb_queue, current_status);
- queue_post(&usb_queue, current_status, 0);
+ last_status = current_status;
+ timeout_register(&tmo, usb_status_tmo_callback, USB_DEBOUNCE_TIME,
+ (intptr_t)&last_status);
restore_irq(oldstatus);
}
}
@@ -644,7 +655,6 @@ void usb_firewire_connect_event(void)
static void usb_tick(void)
{
- #define NUM_POLL_READINGS (HZ/5)
static int usb_countdown = -1;
static int last_usb_status = USB_EXTRACTED;
#ifdef USB_FIREWIRE_HANDLING
@@ -659,7 +669,7 @@ static void usb_tick(void)
if(current_firewire_status != last_firewire_status)
{
last_firewire_status = current_firewire_status;
- firewire_countdown = NUM_POLL_READINGS;
+ firewire_countdown = USB_DEBOUNCE_TIME;
}
else
{
@@ -667,8 +677,7 @@ static void usb_tick(void)
if(firewire_countdown >= 0)
firewire_countdown--;
- /* Report to the thread if we have had 3 identical status
- readings in a row */
+ /* Report status when the signal has been stable long enough */
if(firewire_countdown == 0)
{
queue_post(&usb_queue, USB_REQUEST_REBOOT, 0);
@@ -682,7 +691,7 @@ static void usb_tick(void)
if(current_status != last_usb_status)
{
last_usb_status = current_status;
- usb_countdown = NUM_POLL_READINGS;
+ usb_countdown = USB_DEBOUNCE_TIME;
}
else
{
@@ -690,8 +699,7 @@ static void usb_tick(void)
if(usb_countdown >= 0)
usb_countdown--;
- /* Report to the thread if we have had 3 identical status
- readings in a row */
+ /* Report status when the signal has been stable long enough */
if(usb_countdown == 0)
{
queue_post(&usb_queue, current_status, 0);
diff --git a/firmware/usbstack/usb_class_driver.h b/firmware/usbstack/usb_class_driver.h
index 20ee26a3d0..bffc994d9e 100644
--- a/firmware/usbstack/usb_class_driver.h
+++ b/firmware/usbstack/usb_class_driver.h
@@ -22,6 +22,11 @@
#ifndef _USB_CLASS_DRIVER_H_
#define _USB_CLASS_DRIVER_H_
+#include "usb_ch9.h"
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+
/* Common api, implemented by all class drivers */
struct usb_class_driver {
@@ -75,7 +80,7 @@ struct usb_class_driver {
able to handle it, it should ack the request, and return true. Otherwise
it should return false.
Optional function */
- bool (*control_request)(struct usb_ctrlrequest* req, unsigned char *dest);
+ bool (*control_request)(struct usb_ctrlrequest* req, void* reqdata, unsigned char *dest);
#ifdef HAVE_HOTSWAP
/* Tells the driver that a hotswappable disk/card was inserted or
diff --git a/firmware/usbstack/usb_core.c b/firmware/usbstack/usb_core.c
index 3d187c8cab..9fe8b3e603 100644
--- a/firmware/usbstack/usb_core.c
+++ b/firmware/usbstack/usb_core.c
@@ -22,6 +22,7 @@
#include "thread.h"
#include "kernel.h"
#include "string.h"
+#include "panic.h"
/*#define LOGF_ENABLE*/
#include "logf.h"
@@ -62,6 +63,10 @@
#include "ocotp-imx233.h"
#endif
+#ifdef SANSA_CONNECT
+#include "cryptomem-sansaconnect.h"
+#endif
+
#ifndef USB_MAX_CURRENT
#define USB_MAX_CURRENT 500
#endif
@@ -86,9 +91,9 @@ static struct usb_device_descriptor __attribute__((aligned(2)))
.idVendor = USB_VENDOR_ID,
.idProduct = USB_PRODUCT_ID,
.bcdDevice = 0x0100,
- .iManufacturer = 1,
- .iProduct = 2,
- .iSerialNumber = 3,
+ .iManufacturer = USB_STRING_INDEX_MANUFACTURER,
+ .iProduct = USB_STRING_INDEX_PRODUCT,
+ .iSerialNumber = USB_STRING_INDEX_SERIAL,
.bNumConfigurations = 1
} ;
@@ -137,12 +142,12 @@ static const struct usb_string_descriptor __attribute__((aligned(2)))
lang_descriptor =
USB_STRING_INITIALIZER(u"\x0409"); /* LANGID US English */
-static const struct usb_string_descriptor* const usb_strings[] =
+static const struct usb_string_descriptor* const usb_strings[USB_STRING_INDEX_MAX] =
{
- &lang_descriptor,
- &usb_string_iManufacturer,
- &usb_string_iProduct,
- &usb_string_iSerial
+ [USB_STRING_INDEX_LANGUAGE] = &lang_descriptor,
+ [USB_STRING_INDEX_MANUFACTURER] = &usb_string_iManufacturer,
+ [USB_STRING_INDEX_PRODUCT] = &usb_string_iProduct,
+ [USB_STRING_INDEX_SERIAL] = &usb_string_iSerial,
};
static int usb_address = 0;
@@ -168,7 +173,7 @@ static int usb_no_host_callback(struct timeout *tmo)
static int usb_core_num_interfaces;
typedef void (*completion_handler_t)(int ep, int dir, int status, int length);
-typedef bool (*control_handler_t)(struct usb_ctrlrequest* req,
+typedef bool (*control_handler_t)(struct usb_ctrlrequest* req, void* reqdata,
unsigned char* dest);
static struct
@@ -258,7 +263,15 @@ static struct usb_class_driver drivers[USB_NUM_DRIVERS] =
#endif
};
-static void usb_core_control_request_handler(struct usb_ctrlrequest* req);
+#ifdef USB_LEGACY_CONTROL_API
+static struct usb_ctrlrequest buffered_request;
+static struct usb_ctrlrequest* volatile active_request = NULL;
+static volatile unsigned int num_active_requests = 0;
+static void* volatile control_write_data = NULL;
+static volatile bool control_write_data_done = false;
+#endif
+
+static void usb_core_control_request_handler(struct usb_ctrlrequest* req, void* reqdata);
static unsigned char response_data[256] USB_DEVBSS_ATTR;
@@ -327,6 +340,22 @@ static void set_serial_descriptor(void)
}
usb_string_iSerial.bLength = 2 + 2 * (1 + IMX233_NUM_OCOTP_OPS * 8);
}
+#elif defined(SANSA_CONNECT)
+static void set_serial_descriptor(void)
+{
+ char deviceid[32];
+ short* p = &usb_string_iSerial.wString[1];
+ int i;
+
+ if(!cryptomem_read_deviceid(deviceid)) {
+ for(i = 0; i < 32; i++) {
+ *p++ = deviceid[i];
+ }
+ usb_string_iSerial.bLength = 2 + 2 * (1 + 32);
+ } else {
+ device_descriptor.iSerialNumber = 0;
+ }
+}
#elif (CONFIG_STORAGE & STORAGE_ATA)
/* If we don't know the device serial number, use the one
* from the disk */
@@ -426,10 +455,10 @@ void usb_core_handle_transfer_completion(
case EP_CONTROL:
logf("ctrl handled %ld req=0x%x",
current_tick,
- ((struct usb_ctrlrequest*)event->data)->bRequest);
+ ((struct usb_ctrlrequest*)event->data[0])->bRequest);
usb_core_control_request_handler(
- (struct usb_ctrlrequest*)event->data);
+ (struct usb_ctrlrequest*)event->data[0], event->data[1]);
break;
default:
handler = ep_data[ep].completion_handler[EP_DIR(event->dir)];
@@ -540,7 +569,7 @@ static void allocate_interfaces_and_endpoints(void)
}
-static void control_request_handler_drivers(struct usb_ctrlrequest* req)
+static void control_request_handler_drivers(struct usb_ctrlrequest* req, void* reqdata)
{
int i, interface = req->wIndex & 0xff;
bool handled = false;
@@ -551,7 +580,7 @@ static void control_request_handler_drivers(struct usb_ctrlrequest* req)
drivers[i].first_interface <= interface &&
drivers[i].last_interface > interface)
{
- handled = drivers[i].control_request(req, response_data);
+ handled = drivers[i].control_request(req, reqdata, response_data);
if(handled)
break;
}
@@ -559,11 +588,11 @@ static void control_request_handler_drivers(struct usb_ctrlrequest* req)
if(!handled) {
/* nope. flag error */
logf("bad req:desc %d:%d", req->bRequest, req->wValue >> 8);
- usb_drv_stall(EP_CONTROL, true, true);
+ usb_drv_control_response(USB_CONTROL_STALL, NULL, 0);
}
}
-static void request_handler_device_get_descriptor(struct usb_ctrlrequest* req)
+static void request_handler_device_get_descriptor(struct usb_ctrlrequest* req, void* reqdata)
{
int size;
const void* ptr = NULL;
@@ -617,8 +646,7 @@ static void request_handler_device_get_descriptor(struct usb_ctrlrequest* req)
case USB_DT_STRING:
logf("STRING %d", index);
- if((unsigned)index < (sizeof(usb_strings) /
- sizeof(struct usb_string_descriptor*))) {
+ if((unsigned)index < USB_STRING_INDEX_MAX) {
size = usb_strings[index]->bLength;
ptr = usb_strings[index];
}
@@ -632,7 +660,7 @@ static void request_handler_device_get_descriptor(struct usb_ctrlrequest* req)
}
else {
logf("bad string id %d", index);
- usb_drv_stall(EP_CONTROL, true, true);
+ ptr = NULL;
}
break;
@@ -643,8 +671,8 @@ static void request_handler_device_get_descriptor(struct usb_ctrlrequest* req)
default:
logf("ctrl desc.");
- control_request_handler_drivers(req);
- break;
+ control_request_handler_drivers(req, reqdata);
+ return;
}
if(ptr) {
@@ -654,8 +682,9 @@ static void request_handler_device_get_descriptor(struct usb_ctrlrequest* req)
if (ptr != response_data)
memcpy(response_data, ptr, length);
- usb_drv_recv(EP_CONTROL, NULL, 0);
- usb_drv_send(EP_CONTROL, response_data, length);
+ usb_drv_control_response(USB_CONTROL_ACK, response_data, length);
+ } else {
+ usb_drv_control_response(USB_CONTROL_STALL, NULL, 0);
}
}
@@ -699,105 +728,105 @@ static void usb_core_do_clear_feature(int recip, int recip_nr, int feature)
}
}
-static void request_handler_device(struct usb_ctrlrequest* req)
+static void request_handler_device(struct usb_ctrlrequest* req, void* reqdata)
{
+ unsigned address;
+
switch(req->bRequest) {
- case USB_REQ_GET_CONFIGURATION: {
- logf("usb_core: GET_CONFIG");
- response_data[0] = (usb_state == ADDRESS ? 0 : 1);
- usb_drv_recv(EP_CONTROL, NULL, 0);
- usb_drv_send(EP_CONTROL, response_data, 1);
- break;
- }
- case USB_REQ_SET_CONFIGURATION: {
- usb_drv_cancel_all_transfers();
- usb_core_do_set_config(req->wValue);
- usb_drv_send(EP_CONTROL, NULL, 0);
- break;
- }
- case USB_REQ_SET_ADDRESS: {
- unsigned char address = req->wValue;
- usb_drv_send(EP_CONTROL, NULL, 0);
- usb_drv_cancel_all_transfers();
- usb_drv_set_address(address);
- usb_core_do_set_addr(address);
- break;
- }
+ case USB_REQ_GET_CONFIGURATION:
+ logf("usb_core: GET_CONFIG");
+ response_data[0] = (usb_state == ADDRESS ? 0 : 1);
+ usb_drv_control_response(USB_CONTROL_ACK, response_data, 1);
+ break;
+ case USB_REQ_SET_CONFIGURATION:
+ usb_drv_cancel_all_transfers();
+ usb_core_do_set_config(req->wValue);
+ usb_drv_control_response(USB_CONTROL_ACK, NULL, 0);
+ break;
+ case USB_REQ_SET_ADDRESS:
+ /* NOTE: We really have no business handling this and drivers
+ * should just handle it themselves. We don't care beyond
+ * knowing if we've been assigned an address yet, or not. */
+ address = req->wValue;
+ usb_drv_control_response(USB_CONTROL_ACK, NULL, 0);
+ usb_drv_cancel_all_transfers();
+ usb_drv_set_address(address);
+ usb_core_do_set_addr(address);
+ break;
case USB_REQ_GET_DESCRIPTOR:
logf("usb_core: GET_DESC %d", req->wValue >> 8);
- request_handler_device_get_descriptor(req);
- break;
- case USB_REQ_CLEAR_FEATURE:
+ request_handler_device_get_descriptor(req, reqdata);
break;
case USB_REQ_SET_FEATURE:
if(req->wValue==USB_DEVICE_TEST_MODE) {
int mode = req->wIndex >> 8;
- usb_drv_send(EP_CONTROL, NULL, 0);
+ usb_drv_control_response(USB_CONTROL_ACK, NULL, 0);
usb_drv_set_test_mode(mode);
+ } else {
+ usb_drv_control_response(USB_CONTROL_STALL, NULL, 0);
}
break;
case USB_REQ_GET_STATUS:
response_data[0] = 0;
response_data[1] = 0;
- usb_drv_recv(EP_CONTROL, NULL, 0);
- usb_drv_send(EP_CONTROL, response_data, 2);
+ usb_drv_control_response(USB_CONTROL_ACK, response_data, 2);
break;
default:
logf("bad req:desc %d:%d", req->bRequest, req->wValue);
- usb_drv_stall(EP_CONTROL, true, true);
+ usb_drv_control_response(USB_CONTROL_STALL, NULL, 0);
break;
}
}
-static void request_handler_interface_standard(struct usb_ctrlrequest* req)
+static void request_handler_interface_standard(struct usb_ctrlrequest* req, void* reqdata)
{
switch (req->bRequest)
{
case USB_REQ_SET_INTERFACE:
logf("usb_core: SET_INTERFACE");
- usb_drv_send(EP_CONTROL, NULL, 0);
+ usb_drv_control_response(USB_CONTROL_ACK, NULL, 0);
break;
case USB_REQ_GET_INTERFACE:
logf("usb_core: GET_INTERFACE");
response_data[0] = 0;
- usb_drv_recv(EP_CONTROL, NULL, 0);
- usb_drv_send(EP_CONTROL, response_data, 1);
- break;
- case USB_REQ_CLEAR_FEATURE:
- break;
- case USB_REQ_SET_FEATURE:
+ usb_drv_control_response(USB_CONTROL_ACK, response_data, 1);
break;
case USB_REQ_GET_STATUS:
response_data[0] = 0;
response_data[1] = 0;
- usb_drv_recv(EP_CONTROL, NULL, 0);
- usb_drv_send(EP_CONTROL, response_data, 2);
+ usb_drv_control_response(USB_CONTROL_ACK, response_data, 2);
+ break;
+ case USB_REQ_CLEAR_FEATURE:
+ case USB_REQ_SET_FEATURE:
+ /* TODO: These used to be ignored (erroneously).
+ * Should they be passed to the drivers instead? */
+ usb_drv_control_response(USB_CONTROL_STALL, NULL, 0);
break;
default:
- control_request_handler_drivers(req);
+ control_request_handler_drivers(req, reqdata);
break;
}
}
-static void request_handler_interface(struct usb_ctrlrequest* req)
+static void request_handler_interface(struct usb_ctrlrequest* req, void* reqdata)
{
switch(req->bRequestType & USB_TYPE_MASK) {
case USB_TYPE_STANDARD:
- request_handler_interface_standard(req);
+ request_handler_interface_standard(req, reqdata);
break;
case USB_TYPE_CLASS:
- control_request_handler_drivers(req);
+ control_request_handler_drivers(req, reqdata);
break;
case USB_TYPE_VENDOR:
default:
logf("bad req:desc %d", req->bRequest);
- usb_drv_stall(EP_CONTROL, true, true);
+ usb_drv_control_response(USB_CONTROL_STALL, NULL, 0);
break;
}
}
-static void request_handler_endoint_drivers(struct usb_ctrlrequest* req)
+static void request_handler_endpoint_drivers(struct usb_ctrlrequest* req, void* reqdata)
{
bool handled = false;
control_handler_t control_handler = NULL;
@@ -807,30 +836,30 @@ static void request_handler_endoint_drivers(struct usb_ctrlrequest* req)
ep_data[EP_NUM(req->wIndex)].control_handler[EP_DIR(req->wIndex)];
if(control_handler)
- handled = control_handler(req, response_data);
+ handled = control_handler(req, reqdata, response_data);
if(!handled) {
/* nope. flag error */
logf("usb bad req %d", req->bRequest);
- usb_drv_stall(EP_CONTROL, true, true);
+ usb_drv_control_response(USB_CONTROL_STALL, NULL, 0);
}
}
-static void request_handler_endpoint_standard(struct usb_ctrlrequest* req)
+static void request_handler_endpoint_standard(struct usb_ctrlrequest* req, void* reqdata)
{
switch (req->bRequest) {
case USB_REQ_CLEAR_FEATURE:
usb_core_do_clear_feature(USB_RECIP_ENDPOINT,
req->wIndex,
req->wValue);
- usb_drv_send(EP_CONTROL, NULL, 0);
+ usb_drv_control_response(USB_CONTROL_ACK, NULL, 0);
break;
case USB_REQ_SET_FEATURE:
logf("usb_core: SET FEATURE (%d)", req->wValue);
if(req->wValue == USB_ENDPOINT_HALT)
usb_drv_stall(EP_NUM(req->wIndex), true, EP_DIR(req->wIndex));
-
- usb_drv_send(EP_CONTROL, NULL, 0);
+
+ usb_drv_control_response(USB_CONTROL_ACK, NULL, 0);
break;
case USB_REQ_GET_STATUS:
response_data[0] = 0;
@@ -839,35 +868,34 @@ static void request_handler_endpoint_standard(struct usb_ctrlrequest* req)
if(req->wIndex > 0)
response_data[0] = usb_drv_stalled(EP_NUM(req->wIndex),
EP_DIR(req->wIndex));
-
- usb_drv_recv(EP_CONTROL, NULL, 0);
- usb_drv_send(EP_CONTROL, response_data, 2);
+
+ usb_drv_control_response(USB_CONTROL_ACK, response_data, 2);
break;
default:
- request_handler_endoint_drivers(req);
+ request_handler_endpoint_drivers(req, reqdata);
break;
}
}
-static void request_handler_endpoint(struct usb_ctrlrequest* req)
+static void request_handler_endpoint(struct usb_ctrlrequest* req, void* reqdata)
{
switch(req->bRequestType & USB_TYPE_MASK) {
case USB_TYPE_STANDARD:
- request_handler_endpoint_standard(req);
+ request_handler_endpoint_standard(req, reqdata);
break;
case USB_TYPE_CLASS:
- request_handler_endoint_drivers(req);
+ request_handler_endpoint_drivers(req, reqdata);
break;
case USB_TYPE_VENDOR:
default:
logf("bad req:desc %d", req->bRequest);
- usb_drv_stall(EP_CONTROL, true, true);
+ usb_drv_control_response(USB_CONTROL_STALL, NULL, 0);
break;
}
}
/* Handling USB requests starts here */
-static void usb_core_control_request_handler(struct usb_ctrlrequest* req)
+static void usb_core_control_request_handler(struct usb_ctrlrequest* req, void* reqdata)
{
#ifdef HAVE_USB_CHARGING_ENABLE
timeout_cancel(&usb_no_host_timeout);
@@ -885,20 +913,19 @@ static void usb_core_control_request_handler(struct usb_ctrlrequest* req)
switch(req->bRequestType & USB_RECIP_MASK) {
case USB_RECIP_DEVICE:
- request_handler_device(req);
+ request_handler_device(req, reqdata);
break;
case USB_RECIP_INTERFACE:
- request_handler_interface(req);
+ request_handler_interface(req, reqdata);
break;
case USB_RECIP_ENDPOINT:
- request_handler_endpoint(req);
+ request_handler_endpoint(req, reqdata);
break;
- case USB_RECIP_OTHER:
+ default:
logf("unsupported recipient");
- usb_drv_stall(EP_CONTROL, true, true);
+ usb_drv_control_response(USB_CONTROL_STALL, NULL, 0);
break;
}
- //logf("control handled");
}
/* called by usb_drv_int() */
@@ -908,32 +935,47 @@ void usb_core_bus_reset(void)
usb_address = 0;
usb_state = DEFAULT;
#ifdef HAVE_USB_CHARGING_ENABLE
+#ifdef HAVE_USB_CHARGING_IN_THREAD
+ /* On some targets usb_charging_maxcurrent_change() cannot be called
+ * from an interrupt handler; get the USB thread to do it instead. */
+ usb_charger_update();
+#else
usb_charging_maxcurrent_change(usb_charging_maxcurrent());
#endif
+#endif
}
/* called by usb_drv_transfer_completed() */
void usb_core_transfer_complete(int endpoint, int dir, int status, int length)
{
- struct usb_transfer_completion_event_data *completion_event;
+ struct usb_transfer_completion_event_data* completion_event =
+ &ep_data[endpoint].completion_event[EP_DIR(dir)];
- switch (endpoint) {
- case EP_CONTROL:
- /* already handled */
- break;
+ void* data0 = NULL;
+ void* data1 = NULL;
- default:
- completion_event = &ep_data[endpoint].completion_event[EP_DIR(dir)];
+#ifdef USB_LEGACY_CONTROL_API
+ if(endpoint == EP_CONTROL) {
+ bool cwdd = control_write_data_done;
+ struct usb_ctrlrequest* req = active_request;
- completion_event->endpoint = endpoint;
- completion_event->dir = dir;
- completion_event->data = 0;
- completion_event->status = status;
- completion_event->length = length;
- /* All other endpoints. Let the thread deal with it */
- usb_signal_transfer_completion(completion_event);
- break;
+ if(dir == USB_DIR_OUT && req && cwdd) {
+ data0 = req;
+ data1 = control_write_data;
+ } else {
+ return;
+ }
}
+#endif
+
+ completion_event->endpoint = endpoint;
+ completion_event->dir = dir;
+ completion_event->data[0] = data0;
+ completion_event->data[1] = data1;
+ completion_event->status = status;
+ completion_event->length = length;
+
+ usb_signal_transfer_completion(completion_event);
}
void usb_core_handle_notify(long id, intptr_t data)
@@ -951,21 +993,154 @@ void usb_core_handle_notify(long id, intptr_t data)
}
}
-/* called by usb_drv_int() */
-void usb_core_control_request(struct usb_ctrlrequest* req)
+void usb_core_control_request(struct usb_ctrlrequest* req, void* reqdata)
{
struct usb_transfer_completion_event_data* completion_event =
&ep_data[EP_CONTROL].completion_event[EP_DIR(USB_DIR_IN)];
completion_event->endpoint = EP_CONTROL;
completion_event->dir = 0;
- completion_event->data = (void*)req;
+ completion_event->data[0] = (void*)req;
+ completion_event->data[1] = reqdata;
completion_event->status = 0;
completion_event->length = 0;
logf("ctrl received %ld, req=0x%x", current_tick, req->bRequest);
usb_signal_transfer_completion(completion_event);
}
+void usb_core_control_complete(int status)
+{
+ /* We currently don't use this, it's here to make the API look good ;)
+ * It makes sense to #define it away on normal builds.
+ */
+ (void)status;
+ logf("ctrl complete %ld, %d", current_tick, status);
+}
+
+#ifdef USB_LEGACY_CONTROL_API
+/* Only needed if the driver does not support the new API yet */
+void usb_core_legacy_control_request(struct usb_ctrlrequest* req)
+{
+ /* Only submit non-overlapping requests */
+ if (num_active_requests++ == 0)
+ {
+ buffered_request = *req;
+ active_request = &buffered_request;
+ control_write_data = NULL;
+ control_write_data_done = false;
+
+ usb_core_control_request(req, NULL);
+ }
+}
+
+void usb_drv_control_response(enum usb_control_response resp,
+ void* data, int length)
+{
+ struct usb_ctrlrequest* req = active_request;
+ unsigned int num_active = num_active_requests--;
+
+ /*
+ * There should have been a prior request submission, at least.
+ * FIXME: It seems the iPod video can get here and ignoring it
+ * allows the connection to succeed??
+ */
+ if (num_active == 0)
+ {
+ //panicf("null ctrl req");
+ return;
+ }
+
+ /*
+ * This can happen because an active request was already pending when
+ * the driver submitted a new one in usb_core_legacy_control_request().
+ * This could mean two things: (a) a driver bug; or (b) the host sent
+ * another request because we were too slow in handling an earlier one.
+ *
+ * The USB spec requires we respond to the latest request and drop any
+ * earlier ones, but that's not easy to do with the current design of
+ * the USB stack. Thus, the host will be expecting a response for the
+ * latest request, but this response is for the _earliest_ request.
+ *
+ * Play it safe and return a STALL. At this point we've recovered from
+ * the error on our end and will be ready to handle the next request.
+ */
+ if (num_active > 1)
+ {
+ active_request = NULL;
+ num_active_requests = 0;
+ usb_drv_stall(EP_CONTROL, true, true);
+ return;
+ }
+
+ if(req->wLength == 0)
+ {
+ active_request = NULL;
+
+ /* No-data request */
+ if(resp == USB_CONTROL_ACK)
+ usb_drv_send(EP_CONTROL, data, length);
+ else if(resp == USB_CONTROL_STALL)
+ usb_drv_stall(EP_CONTROL, true, true);
+ else
+ panicf("RECEIVE on non-data req");
+ }
+ else if(req->bRequestType & USB_DIR_IN)
+ {
+ /* Control read request */
+ if(resp == USB_CONTROL_ACK)
+ {
+ active_request = NULL;
+ usb_drv_recv_nonblocking(EP_CONTROL, NULL, 0);
+ usb_drv_send(EP_CONTROL, data, length);
+ }
+ else if(resp == USB_CONTROL_STALL)
+ {
+ active_request = NULL;
+ usb_drv_stall(EP_CONTROL, true, true);
+ }
+ else
+ {
+ panicf("RECEIVE on ctrl read req");
+ }
+ }
+ else if(!control_write_data_done)
+ {
+ /* Control write request, data phase */
+ if(resp == USB_CONTROL_RECEIVE)
+ {
+ control_write_data = data;
+ control_write_data_done = true;
+ usb_drv_recv_nonblocking(EP_CONTROL, data, length);
+ }
+ else if(resp == USB_CONTROL_STALL)
+ {
+ /* We should stall the OUT endpoint here, but the old code did
+ * not do so and some drivers may not handle it correctly. */
+ active_request = NULL;
+ usb_drv_stall(EP_CONTROL, true, true);
+ }
+ else
+ {
+ panicf("ACK on ctrl write data");
+ }
+ }
+ else
+ {
+ active_request = NULL;
+ control_write_data = NULL;
+ control_write_data_done = false;
+
+ /* Control write request, status phase */
+ if(resp == USB_CONTROL_ACK)
+ usb_drv_send(EP_CONTROL, NULL, 0);
+ else if(resp == USB_CONTROL_STALL)
+ usb_drv_stall(EP_CONTROL, true, true);
+ else
+ panicf("RECEIVE on ctrl write status");
+ }
+}
+#endif
+
void usb_core_notify_set_address(uint8_t addr)
{
logf("notify set addr received %ld", current_tick);
diff --git a/firmware/usbstack/usb_hid.c b/firmware/usbstack/usb_hid.c
index 5885b60e32..64aa123ced 100644
--- a/firmware/usbstack/usb_hid.c
+++ b/firmware/usbstack/usb_hid.c
@@ -664,10 +664,9 @@ void usb_hid_transfer_complete(int ep, int dir, int status, int length)
* In order to allow sending info to the DAP, the Set Report mechanism can be
* used by defining vendor specific output reports and send them from the host
* to the DAP using the host's custom driver */
-static int usb_hid_set_report(struct usb_ctrlrequest *req)
+static int usb_hid_set_report(struct usb_ctrlrequest *req, void *reqdata)
{
- static unsigned char buf[SET_REPORT_BUF_LEN] USB_DEVBSS_ATTR
- __attribute__((aligned(32)));
+ static unsigned char buf[64] USB_DEVBSS_ATTR __attribute__((aligned(32)));
int length;
if ((req->wValue >> 8) != REPORT_TYPE_OUTPUT)
@@ -692,8 +691,11 @@ static int usb_hid_set_report(struct usb_ctrlrequest *req)
return 4;
}
- memset(buf, 0, length);
- usb_drv_recv(EP_CONTROL, buf, length);
+ if(!reqdata) {
+ memset(buf, 0, length);
+ usb_drv_control_response(USB_CONTROL_RECEIVE, buf, length);
+ return 0;
+ }
#ifdef LOGF_ENABLE
if (buf[1] & 0x01)
@@ -710,10 +712,11 @@ static int usb_hid_set_report(struct usb_ctrlrequest *req)
/* Defining other LEDs and setting them from the USB host (OS) can be used
* to send messages to the DAP */
+ usb_drv_control_response(USB_CONTROL_ACK, NULL, 0);
return 0;
}
-static int usb_hid_get_report(struct usb_ctrlrequest *req, unsigned char** dest)
+static int usb_hid_get_report(struct usb_ctrlrequest *req, unsigned char* dest)
{
if ((req->wValue >> 8) != REPORT_TYPE_FEATURE)
{
@@ -739,16 +742,17 @@ static int usb_hid_get_report(struct usb_ctrlrequest *req, unsigned char** dest)
return 4;
}
- (*dest)[0] = 0;
- (*dest)[1] = battery_level();
- *dest += GET_REPORT_BUF_LEN;
-
+ dest[0] = 0;
+ dest[1] = battery_level();
+ usb_drv_control_response(USB_CONTROL_ACK, dest, 2);
return 0;
}
/* called by usb_core_control_request() */
-bool usb_hid_control_request(struct usb_ctrlrequest *req, unsigned char *dest)
+bool usb_hid_control_request(struct usb_ctrlrequest *req, void *reqdata, unsigned char *dest)
{
+ (void)reqdata;
+
unsigned char *orig_dest = dest;
switch (req->bRequestType & USB_TYPE_MASK)
{
@@ -772,8 +776,7 @@ bool usb_hid_control_request(struct usb_ctrlrequest *req, unsigned char *dest)
if (dest != orig_dest)
{
- usb_drv_recv(EP_CONTROL, NULL, 0); /* ack */
- usb_drv_send(EP_CONTROL, orig_dest, dest - orig_dest);
+ usb_drv_control_response(USB_CONTROL_ACK, orig_dest, dest - orig_dest);
return true;
}
break;
@@ -785,27 +788,26 @@ bool usb_hid_control_request(struct usb_ctrlrequest *req, unsigned char *dest)
(req->bRequest == USB_HID_SET_IDLE) ? "set idle" :
((req->bRequest == USB_HID_SET_REPORT) ? "set report" :
((req->bRequest == USB_HID_GET_REPORT) ? "get report" : "")));
+
+ int rc;
switch (req->bRequest)
{
case USB_HID_SET_REPORT:
- if (usb_hid_set_report(req))
- break;
+ rc = usb_hid_set_report(req, reqdata);
+ break;
case USB_HID_GET_REPORT:
- if (usb_hid_get_report(req, &dest))
- break;
+ rc = usb_hid_get_report(req, dest);
+ break;
case USB_HID_SET_IDLE:
- if (dest != orig_dest)
- {
- usb_drv_recv(EP_CONTROL, NULL, 0); /* ack */
- usb_drv_send(EP_CONTROL, orig_dest, dest - orig_dest);
- }
- else
- {
- usb_drv_send(EP_CONTROL, NULL, 0); /* ack */
- }
+ usb_drv_control_response(USB_CONTROL_ACK, NULL, 0);
return true;
+ default:
+ /* all other requests are errors */
+ return false;
}
- break;
+
+ if(rc == 0)
+ return true;
}
case USB_TYPE_VENDOR:
diff --git a/firmware/usbstack/usb_hid.h b/firmware/usbstack/usb_hid.h
index 917992cd35..41e6662e29 100644
--- a/firmware/usbstack/usb_hid.h
+++ b/firmware/usbstack/usb_hid.h
@@ -32,7 +32,7 @@ void usb_hid_init_connection(void);
void usb_hid_init(void);
void usb_hid_disconnect(void);
void usb_hid_transfer_complete(int ep, int dir, int status, int length);
-bool usb_hid_control_request(struct usb_ctrlrequest* req, unsigned char* dest);
+bool usb_hid_control_request(struct usb_ctrlrequest* req, void* reqdata, unsigned char* dest);
void usb_hid_send(usage_page_t usage_page, int id);
diff --git a/firmware/usbstack/usb_serial.c b/firmware/usbstack/usb_serial.c
index d879dc7c99..ae90b57078 100644
--- a/firmware/usbstack/usb_serial.c
+++ b/firmware/usbstack/usb_serial.c
@@ -8,6 +8,7 @@
* $Id$
*
* Copyright (C) 2007 by Christian Gmeiner
+ * Copyright (C) 2021 by Tomasz Moń
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -28,9 +29,128 @@
/*#define LOGF_ENABLE*/
#include "logf.h"
-/* serial interface */
-static struct usb_interface_descriptor __attribute__((aligned(2)))
- interface_descriptor =
+#define CDC_SUBCLASS_ACM 0x02
+#define CDC_PROTOCOL_NONE 0x00
+
+/* Class-Specific Request Codes */
+#define SET_LINE_CODING 0x20
+#define GET_LINE_CODING 0x21
+#define SET_CONTROL_LINE_STATE 0x22
+
+#define SUBTYPE_HEADER 0x00
+#define SUBTYPE_CALL_MANAGEMENT 0x01
+#define SUBTYPE_ACM 0x02
+#define SUBTYPE_UNION 0x06
+
+/* Support SET_LINE_CODING, GET_LINE_CODING, SET_CONTROL_LINE_STATE requests
+ * and SERIAL_STATE notification.
+ */
+#define ACM_CAP_LINE_CODING 0x02
+
+struct cdc_header_descriptor {
+ uint8_t bFunctionLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubtype;
+ uint16_t bcdCDC;
+} __attribute__((packed));
+
+struct cdc_call_management_descriptor {
+ uint8_t bFunctionLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubtype;
+ uint8_t bmCapabilities;
+ uint8_t bDataInterface;
+} __attribute__((packed));
+
+struct cdc_acm_descriptor {
+ uint8_t bFunctionLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubtype;
+ uint8_t bmCapabilities;
+} __attribute__((packed));
+
+struct cdc_union_descriptor {
+ uint8_t bFunctionLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubtype;
+ uint8_t bControlInterface;
+ uint8_t bSubordinateInterface0;
+} __attribute__((packed));
+
+struct cdc_line_coding {
+ uint32_t dwDTERate;
+ uint8_t bCharFormat;
+ uint8_t bParityType;
+ uint8_t bDataBits;
+} __attribute__((packed));
+
+static struct usb_interface_assoc_descriptor
+ association_descriptor =
+{
+ .bLength = sizeof(struct usb_interface_assoc_descriptor),
+ .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
+ .bFirstInterface = 0,
+ .bInterfaceCount = 2,
+ .bFunctionClass = USB_CLASS_COMM,
+ .bFunctionSubClass = CDC_SUBCLASS_ACM,
+ .bFunctionProtocol = CDC_PROTOCOL_NONE,
+ .iFunction = 0
+};
+
+static struct usb_interface_descriptor
+ control_interface_descriptor =
+{
+ .bLength = sizeof(struct usb_interface_descriptor),
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = USB_CLASS_COMM,
+ .bInterfaceSubClass = CDC_SUBCLASS_ACM,
+ .bInterfaceProtocol = CDC_PROTOCOL_NONE,
+ .iInterface = 0
+};
+
+static struct cdc_header_descriptor
+ header_descriptor =
+{
+ .bFunctionLength = sizeof(struct cdc_header_descriptor),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = SUBTYPE_HEADER,
+ .bcdCDC = 0x0110
+};
+
+static struct cdc_call_management_descriptor
+ call_management_descriptor =
+{
+ .bFunctionLength = sizeof(struct cdc_call_management_descriptor),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = SUBTYPE_CALL_MANAGEMENT,
+ .bmCapabilities = 0,
+ .bDataInterface = 0
+};
+
+static struct cdc_acm_descriptor
+ acm_descriptor =
+{
+ .bFunctionLength = sizeof(struct cdc_acm_descriptor),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = SUBTYPE_ACM,
+ .bmCapabilities = ACM_CAP_LINE_CODING
+};
+
+static struct cdc_union_descriptor
+ union_descriptor =
+{
+ .bFunctionLength = sizeof(struct cdc_union_descriptor),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = SUBTYPE_UNION,
+ .bControlInterface = 0,
+ .bSubordinateInterface0 = 0
+};
+
+static struct usb_interface_descriptor
+ data_interface_descriptor =
{
.bLength = sizeof(struct usb_interface_descriptor),
.bDescriptorType = USB_DT_INTERFACE,
@@ -43,8 +163,7 @@ static struct usb_interface_descriptor __attribute__((aligned(2)))
.iInterface = 0
};
-
-static struct usb_endpoint_descriptor __attribute__((aligned(2)))
+static struct usb_endpoint_descriptor
endpoint_descriptor =
{
.bLength = sizeof(struct usb_endpoint_descriptor),
@@ -55,6 +174,14 @@ static struct usb_endpoint_descriptor __attribute__((aligned(2)))
.bInterval = 0
};
+union line_coding_buffer
+{
+ struct cdc_line_coding data;
+ unsigned char raw[64];
+};
+
+static union line_coding_buffer line_coding USB_DEVBSS_ATTR;
+
/* send_buffer: local ring buffer.
* transit_buffer: used to store aligned data that will be sent by the USB
* driver. PP502x needs boost for high speed USB, but still works up to
@@ -63,10 +190,11 @@ static struct usb_endpoint_descriptor __attribute__((aligned(2)))
*/
#define BUFFER_SIZE 512
#define TRANSIT_BUFFER_SIZE 32
+#define RECV_BUFFER_SIZE 32
static unsigned char send_buffer[BUFFER_SIZE];
static unsigned char transit_buffer[TRANSIT_BUFFER_SIZE]
USB_DEVBSS_ATTR __attribute__((aligned(4)));
-static unsigned char receive_buffer[32]
+static unsigned char receive_buffer[512]
USB_DEVBSS_ATTR __attribute__((aligned(32)));
static void sendout(void);
@@ -78,8 +206,8 @@ static int buffer_length;
static int buffer_transitlength;
static bool active = false;
-static int ep_in, ep_out;
-static int usb_interface;
+static int ep_in, ep_out, ep_int;
+static int control_interface, data_interface;
int usb_serial_request_endpoints(struct usb_class_driver *drv)
{
@@ -94,25 +222,59 @@ int usb_serial_request_endpoints(struct usb_class_driver *drv)
return -1;
}
+ /* Optional interrupt endpoint. While the code does not actively use it,
+ * it is needed to get out-of-the-box serial port experience on Windows
+ * and Linux. If this endpoint is not available, only CDC Data interface
+ * will be exported (can still work on Linux with manual modprobe).
+ */
+ ep_int = usb_core_request_endpoint(USB_ENDPOINT_XFER_INT, USB_DIR_IN, drv);
+
return 0;
}
int usb_serial_set_first_interface(int interface)
{
- usb_interface = interface;
- return interface + 1;
+ control_interface = interface;
+ data_interface = interface + 1;
+ return interface + 2;
}
int usb_serial_get_config_descriptor(unsigned char *dest, int max_packet_size)
{
unsigned char *orig_dest = dest;
- interface_descriptor.bInterfaceNumber = usb_interface;
- PACK_DATA(&dest, interface_descriptor);
+ association_descriptor.bFirstInterface = control_interface;
+ control_interface_descriptor.bInterfaceNumber = control_interface;
+ call_management_descriptor.bDataInterface = data_interface;
+ union_descriptor.bControlInterface = control_interface;
+ union_descriptor.bSubordinateInterface0 = data_interface;
+ data_interface_descriptor.bInterfaceNumber = data_interface;
- endpoint_descriptor.wMaxPacketSize = max_packet_size;
+ if (ep_int > 0)
+ {
+ PACK_DATA(&dest, association_descriptor);
+ PACK_DATA(&dest, control_interface_descriptor);
+ PACK_DATA(&dest, header_descriptor);
+ PACK_DATA(&dest, call_management_descriptor);
+ PACK_DATA(&dest, acm_descriptor);
+ PACK_DATA(&dest, union_descriptor);
+
+ /* Notification endpoint. Set wMaxPacketSize to 64 as it is valid
+ * both on Full and High speed. Note that max_packet_size is for bulk.
+ * Maximum bInterval for High Speed is 16 and for Full Speed is 255.
+ */
+ endpoint_descriptor.bEndpointAddress = ep_int;
+ endpoint_descriptor.bmAttributes = USB_ENDPOINT_XFER_INT;
+ endpoint_descriptor.wMaxPacketSize = 64;
+ endpoint_descriptor.bInterval = 16;
+ PACK_DATA(&dest, endpoint_descriptor);
+ }
+ PACK_DATA(&dest, data_interface_descriptor);
endpoint_descriptor.bEndpointAddress = ep_in;
+ endpoint_descriptor.bmAttributes = USB_ENDPOINT_XFER_BULK;
+ endpoint_descriptor.wMaxPacketSize = max_packet_size;
+ endpoint_descriptor.bInterval = 0;
PACK_DATA(&dest, endpoint_descriptor);
endpoint_descriptor.bEndpointAddress = ep_out;
@@ -122,22 +284,69 @@ int usb_serial_get_config_descriptor(unsigned char *dest, int max_packet_size)
}
/* called by usb_core_control_request() */
-bool usb_serial_control_request(struct usb_ctrlrequest* req, unsigned char* dest)
+bool usb_serial_control_request(struct usb_ctrlrequest* req, void* reqdata, unsigned char* dest)
{
bool handled = false;
(void)dest;
- switch (req->bRequest) {
- default:
- logf("serial: unhandeld req %d", req->bRequest);
+ (void)reqdata;
+
+ if (req->wIndex != control_interface)
+ {
+ return false;
}
+
+ if (req->bRequestType == (USB_DIR_OUT|USB_TYPE_CLASS|USB_RECIP_INTERFACE))
+ {
+ if (req->bRequest == SET_LINE_CODING)
+ {
+ if (req->wLength == sizeof(struct cdc_line_coding))
+ {
+ /* Receive line coding into local copy */
+ if (!reqdata)
+ {
+ usb_drv_control_response(USB_CONTROL_RECEIVE, line_coding.raw,
+ sizeof(struct cdc_line_coding));
+ }
+ else
+ {
+ usb_drv_control_response(USB_CONTROL_ACK, NULL, 0);
+ }
+
+ handled = true;
+ }
+ }
+ else if (req->bRequest == SET_CONTROL_LINE_STATE)
+ {
+ if (req->wLength == 0)
+ {
+ /* wValue holds Control Signal Bitmap that is simply ignored here */
+ usb_drv_control_response(USB_CONTROL_ACK, NULL, 0);
+ handled = true;
+ }
+ }
+ }
+ else if (req->bRequestType == (USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE))
+ {
+ if (req->bRequest == GET_LINE_CODING)
+ {
+ if (req->wLength == sizeof(struct cdc_line_coding))
+ {
+ /* Send back line coding so host is happy */
+ usb_drv_control_response(USB_CONTROL_ACK, line_coding.raw,
+ sizeof(struct cdc_line_coding));
+ handled = true;
+ }
+ }
+ }
+
return handled;
}
void usb_serial_init_connection(void)
{
/* prime rx endpoint */
- usb_drv_recv(ep_out, receive_buffer, sizeof receive_buffer);
+ usb_drv_recv_nonblocking(ep_out, receive_buffer, RECV_BUFFER_SIZE);
/* we come here too after a bus reset, so reset some data */
buffer_transitlength = 0;
@@ -228,7 +437,7 @@ void usb_serial_transfer_complete(int ep,int dir, int status, int length)
/* Data received. TODO : Do something with it ? */
/* Get the next bit */
- usb_drv_recv(ep_out, receive_buffer, sizeof receive_buffer);
+ usb_drv_recv_nonblocking(ep_out, receive_buffer, RECV_BUFFER_SIZE);
break;
case USB_DIR_IN:
diff --git a/firmware/usbstack/usb_serial.h b/firmware/usbstack/usb_serial.h
index f1a603c4a3..c4c0e78724 100644
--- a/firmware/usbstack/usb_serial.h
+++ b/firmware/usbstack/usb_serial.h
@@ -30,7 +30,7 @@ void usb_serial_init_connection(void);
void usb_serial_init(void);
void usb_serial_disconnect(void);
void usb_serial_transfer_complete(int ep,int dir, int status, int length);
-bool usb_serial_control_request(struct usb_ctrlrequest* req, unsigned char *dest);
+bool usb_serial_control_request(struct usb_ctrlrequest* req, void* reqdata, unsigned char *dest);
void usb_serial_send(const unsigned char *data, int length);
diff --git a/firmware/usbstack/usb_storage.c b/firmware/usbstack/usb_storage.c
index c42cc830ee..714af9d535 100644
--- a/firmware/usbstack/usb_storage.c
+++ b/firmware/usbstack/usb_storage.c
@@ -47,7 +47,7 @@
#endif
#ifndef USBSTOR_WRITE_SECTORS_FILTER
-#define USBSTOR_WRITE_SECTORS_FILTER() ({ 0; })
+#define USBSTOR_WRITE_SECTORS_FILTER() ({ 0; })
#endif
/* the ARC driver currently supports up to 64k USB transfers. This is
@@ -71,7 +71,7 @@
#endif /* USB_READ_BUFFER_SIZE */
/* We don't use sizeof() here, because we *need* a multiple of 32 */
-#define MAX_CBW_SIZE 32
+#define MAX_CBW_SIZE 512
#ifdef USB_WRITE_BUFFER_SIZE
#define WRITE_BUFFER_SIZE USB_WRITE_BUFFER_SIZE
@@ -433,7 +433,7 @@ void usb_storage_init_connection(void)
/* prime rx endpoint. We only need room for commands */
state = WAITING_FOR_COMMAND;
-#if (CONFIG_CPU == IMX31L || defined(CPU_TCC77X) || defined(CPU_TCC780X) || \
+#if (CONFIG_CPU == IMX31L || defined(CPU_TCC780X) || \
CONFIG_CPU == S5L8702 || CONFIG_CPU == S5L8701 || CONFIG_CPU == AS3525v2 || \
defined(BOOTLOADER) || CONFIG_CPU == DM320) && !defined(CPU_PP502x)
static unsigned char _cbw_buffer[MAX_CBW_SIZE]
@@ -449,12 +449,10 @@ void usb_storage_init_connection(void)
#endif
#else
unsigned char * buffer;
- /* dummy ops with no callbacks, needed because by
- * default buflib buffers can be moved around which must be avoided */
- static struct buflib_callbacks dummy_ops;
// Add 31 to handle worst-case misalignment
- usb_handle = core_alloc_ex("usb storage", ALLOCATE_BUFFER_SIZE + MAX_CBW_SIZE + 31, &dummy_ops);
+ usb_handle = core_alloc_ex(ALLOCATE_BUFFER_SIZE + MAX_CBW_SIZE + 31,
+ &buflib_ops_locked);
if (usb_handle < 0)
panicf("%s(): OOM", __func__);
@@ -470,7 +468,7 @@ void usb_storage_init_connection(void)
ramdisk_buffer = tb.transfer_buffer + ALLOCATE_BUFFER_SIZE;
#endif
#endif
- usb_drv_recv(ep_out, cbw_buffer, MAX_CBW_SIZE);
+ usb_drv_recv_nonblocking(ep_out, cbw_buffer, MAX_CBW_SIZE);
int i;
for(i=0;i<storage_num_drives();i++) {
@@ -482,8 +480,7 @@ void usb_storage_init_connection(void)
void usb_storage_disconnect(void)
{
- if (usb_handle > 0)
- usb_handle = core_free(usb_handle);
+ usb_handle = core_free(usb_handle);
}
/* called by usb_core_transfer_complete() */
@@ -589,17 +586,6 @@ void usb_storage_transfer_complete(int ep,int dir,int status,int length)
}
handle_scsi(cbw);
break;
-#if 0
- if(cur_cmd.cur_cmd == SCSI_WRITE_10)
- {
- queue_broadcast(SYS_USB_WRITE_DATA, (cur_cmd.lun<<16)+cur_cmd.orig_count);
- }
- else if(cur_cmd.cur_cmd == SCSI_READ_10)
- {
- queue_broadcast(SYS_USB_READ_DATA, (cur_cmd.lun<<16)+cur_cmd.orig_count);
- }
-#endif
- break;
case SENDING_RESULT:
if(dir==USB_DIR_OUT) {
logf("OUT received in SENDING");
@@ -673,11 +659,13 @@ void usb_storage_transfer_complete(int ep,int dir,int status,int length)
}
/* called by usb_core_control_request() */
-bool usb_storage_control_request(struct usb_ctrlrequest* req, unsigned char* dest)
+bool usb_storage_control_request(struct usb_ctrlrequest* req, void* reqdata, unsigned char* dest)
{
bool handled = false;
(void)dest;
+ (void)reqdata;
+
switch (req->bRequest) {
case USB_BULK_GET_MAX_LUN: {
*tb.max_lun = storage_num_drives() - 1;
@@ -685,8 +673,7 @@ bool usb_storage_control_request(struct usb_ctrlrequest* req, unsigned char* des
if(skip_first) (*tb.max_lun) --;
#endif
logf("ums: getmaxlun");
- usb_drv_recv(EP_CONTROL, NULL, 0); /* ack */
- usb_drv_send(EP_CONTROL, tb.max_lun, 1);
+ usb_drv_control_response(USB_CONTROL_ACK, tb.max_lun, 1);
handled = true;
break;
}
@@ -701,7 +688,7 @@ bool usb_storage_control_request(struct usb_ctrlrequest* req, unsigned char* des
usb_drv_reset_endpoint(ep_in, false);
usb_drv_reset_endpoint(ep_out, true);
#endif
- usb_drv_send(EP_CONTROL, NULL, 0); /* ack */
+ usb_drv_control_response(USB_CONTROL_ACK, NULL, 0);
handled = true;
break;
}
@@ -1187,14 +1174,14 @@ static void send_command_failed_result(void)
#if CONFIG_RTC
static void receive_time(void)
{
- usb_drv_recv(ep_out, tb.transfer_buffer, 12);
+ usb_drv_recv_nonblocking(ep_out, tb.transfer_buffer, 12);
state = RECEIVING_TIME;
}
#endif /* CONFIG_RTC */
static void receive_block_data(void *data,int size)
{
- usb_drv_recv(ep_out, data, size);
+ usb_drv_recv_nonblocking(ep_out, data, size);
state = RECEIVING_BLOCKS;
}
@@ -1210,7 +1197,7 @@ static void send_csw(int status)
state = WAITING_FOR_CSW_COMPLETION_OR_COMMAND;
//logf("CSW: %X",status);
/* Already start waiting for the next command */
- usb_drv_recv(ep_out, cbw_buffer, MAX_CBW_SIZE);
+ usb_drv_recv_nonblocking(ep_out, cbw_buffer, MAX_CBW_SIZE);
/* The next completed transfer will be either the CSW one
* or the new command */
@@ -1245,7 +1232,7 @@ static void fill_inquiry(IF_MD_NONVOID(int lun))
tb.inquiry->DeviceType = DIRECT_ACCESS_DEVICE;
tb.inquiry->AdditionalLength = 0x1f;
- memset(tb.inquiry->Reserved, 0, 3);
+// memset(tb.inquiry->Reserved, 0, sizeof(tb.inquiry->Reserved)); // Redundant
tb.inquiry->Versions = 4; /* SPC-2 */
tb.inquiry->Format = 2; /* SPC-2/3 inquiry format */
diff --git a/firmware/usbstack/usb_storage.h b/firmware/usbstack/usb_storage.h
index 3591d285d8..af17689110 100644
--- a/firmware/usbstack/usb_storage.h
+++ b/firmware/usbstack/usb_storage.h
@@ -30,7 +30,7 @@ void usb_storage_init_connection(void);
void usb_storage_disconnect(void);
void usb_storage_init(void);
void usb_storage_transfer_complete(int ep,int dir,int state,int length);
-bool usb_storage_control_request(struct usb_ctrlrequest* req, unsigned char* dest);
+bool usb_storage_control_request(struct usb_ctrlrequest* req, void* reqdata, unsigned char* dest);
#ifdef HAVE_HOTSWAP
void usb_storage_notify_hotswap(int volume,bool inserted);
#endif