Rockbox Technical Forums
Rockbox Development => Starting Development and Compiling => Topic started by: dconrad on June 13, 2021, 06:20:02 PM
-
With respect to amachronic's post (https://forums.rockbox.org/index.php/topic,53601.msg248763.html#msg248763) in the Eros Q/K thread about how to go about reverse engineering the X1000 linux kernel, I thought I might start a dedicated thread if I'm going to keep pursuing this. I'm pretty sure I will have lots of really basic/beginner questions, and should probably put them here rather than stay in that thread where they're probably off-topic. And besides, maybe they'll help somebody in the future anyway.
To be honest, I don't expect to fully complete this on my own, but I thought I might get the ball rolling, and see if we make any progress.
So far I've successfully pulled out the following files from the rockbox-bootloader-patched update file from the ErosQ/K wiki page H2-v13-patched.upt. I'm using this one only because that's the particular device I have.
Out of the .upt file (which, it turns out, is really just an ISO with a different file extension), I got:
VERSION.TXT
UPDATE.TXT
SYSTEM.UBI
UIMAGE.BIN
UBOOT.BIN
_GITIGNO
So far so good, right?
Then I extracted the UIMAGE.BIN file as recommended with binwalk -e and got:
[user@localhost erosq kernel]$ binwalk -e UIMAGE.BIN
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 uImage header, header size: 64 bytes, header CRC: 0xD30BDBEF, created: 2020-03-09 06:41:44, image size:
2612785 bytes, Data Address: 0x80010000, Entry Point: 0x80419300, data CRC: 0xF4F43C27, OS: Linux, CPU: MIPS, image type: OS Kernel Image,
compression type: gzip, image name: "Linux-3.10.14"
64 0x40 gzip compressed data, maximum compression, from Unix, last modified: 1970-01-01 00:00:00 (null date)
[user@localhost erosq kernel]$
This spit out a 5.4 MiB file called "40" (which I suppose is the address it found it at). I imported this into Ghidra with the recommended MIPS LE 32-bit architecture and base address of 0x80010000. Ghidra seemed to like this fine, and took maybe 15-20 minutes to crunch through the file when I imported it. (my computer is old)
So my first sanity check question - it looks like there's a whole lot of what I think are no-ops before the first function, does this look right? I'll attach a couple screenshots to show what I'm getting. Am I on the right track?
-
hmm. Looks like you have a u-boot image there as well, and this kernel isn't self-decompressing (u-boot is doing that instead). You can pull the kernel entry address out of the uImage header in this case...
/* xImage */
00000000 27 05 19 56 ee 20 85 b0 5d c5 14 1d 00 33 b0 00 |'..V. ..]....3..|
00000010 80 f0 00 00 80 f0 00 00 2a d2 2d 06 05 05 02 00 |........*.-.....|
^^ startup ^^ and entry address here
00000020 4c 69 6e 75 78 2d 33 2e 31 30 2e 31 34 2d 73 76 |Linux-3.10.14-sv|
00000030 6e 32 39 36 00 00 00 00 00 00 00 00 00 00 00 00 |n296............|
Not too sure which is what. It should be obvious in your case because the entry address must be higher than the load address. The kernel entry is in arch/mips/kernel/head.S, fittingly called kernel_entry. It's assembly so you can verify you got it by comparing the instructions. The function you're looking at isn't it. Control will transfer to C code by jumping to start_kernel at the end of kernel_entry and that'll give you a known starting point.
To give you an idea of something you're looking for -- the goal is not to disassemble the entire kernel after all ;) -- check out this file: arch/mips/xburst/soc-x1000/chip-x1000/halley2/common/board_base.c. There's a function in it called board_base_init(). That's a function you want to locate, because it's the only place you can find a reference to platform_devices_array. Most platform_data structs which driver probe() functions will reference, are pointed to by this array, and from the platform_data struct you can learn GPIOs, details of the LCD interface configuration, clock speeds, and other useful stuff. I2C devices will probably be registered by board_base_init too. But some stuff will be hardcoded willy-nilly in the drivers. (I guess for a kernel you never update, maintainability is not a problem...)
Since board_base_init is an initcall it can be located in a table. See init/main.c, look for the initcall_levels array. And then look at the initcall_level_names below it. You can easily find the names array because it references a unique string "postcore". Once you find that you can find do_initcall_level() and from that, find initcall_levels. The initcalls are set up in one big table and the first entry of initcall_levels will point at the first one. You can trawl through the init calls one by one until you locate board_base_init(). That may require identifying a few other functions first, but the payoff is worth it because some information simply can't be found any other way.
Some other random notes:
- Try to identify BUG_ON() and WARN_ON() statements. They are macros so this requires a bit of digging in the Linux source to match it up with the binary, but they can give you exact file and line number information and the source files using those macros are unlikely to have been changed. Many Linux API functions can be discovered this way.
- You can find many GPIOs by identifying gpio_request(). A lot of the gpiolib functions contain code like printk(..., __FUNC__), and you can identify most of them that way very easily.
- If you have dmesg output from the OF kernel, you can try looking for interesting messages that pop up there and seeing what code prints them.
- LCD code: kernel/arch/mips/xburst/soc-x1000/chip-x1000/halley2/common/lcd/lcd-truly_tft240240_2_e.c. See how the platform data points to useful things... you want to find stuff like that.
Look at my Shanling Q1 patch in gerrit for an idea of what kind of information you actually need to grab. I think most, if not all of the chips you're dealing with have decent datasheets so finding the GPIOs and LCD related stuff is the most important. I2C bus numbers are useful too, but then again, you can just brute force them if you really have to, as long as you know what chips are supposed to be there.
-
Some small progress I think:
So if I understand correctly, the kernel entry address given by the uimage header (read out when I used binwalk) is 0x80419300, and I can use this to directly find the corresponding address in the kernel. If I go to that address, it seems to match up, more or less, with this line (https://github.com/YuanhuanLiang/X1000/blob/master/kernel/arch/mips/kernel/head.S#L149) in the generic source. (see screenshot)
Or, more accurately, I think that line 185 (https://github.com/YuanhuanLiang/X1000/blob/master/kernel/arch/mips/kernel/head.S#L185) lines up with address 0x80419340
PTR_ADDIU t0, LONGSIZE
LONG_S zero, (t0)
bne t0, t1, 1b
LONG_S a0, fw_arg0
LONG_S a1, fw_arg1
LONG_S a2, fw_arg2
LONG_S a3, fw_arg3
MTC0 zero, CP0_CONTEXT
...
...
...
j start_kernel
LAB_80419340 XREF[1]: 80419348(j)
80419340 04 00 08 25 addiu t0,t0,0x4
80419344 00 00 00 ad sw zero,0x0(t0)=>DAT_80580004
80419348 fd ff 09 15 bne t0,t1,LAB_80419340
8041934c 00 00 00 00 _nop
80419350 5a 80 01 3c lui at,0x805a
80419354 20 23 24 ac sw a0,offset DAT_805a2320(at) --> (matches up with LONG_S a0, fw_arg0)
80419358 5a 80 01 3c lui at,0x805a
8041935c 1c 23 25 ac sw a1,offset DAT_805a231c(at) --> (matches up with LONG_S a1, fw_arg1)
80419360 5a 80 01 3c lui at,0x805a
80419364 18 23 26 ac sw a2,offset DAT_805a2318(at) --> (etc.)
80419368 5a 80 01 3c lui at,0x805a
8041936c 14 23 27 ac sw a3,offset DAT_805a2314(at) --> (etc.)
80419370 00 20 80 40 mtc0 zero,Context,0x0
...
...
...
804193a0 68 31 15 08 j FUN_8054c5a0 undefined FUN_8054c5a0()
It also seems that the fact that the kernel was gzipped inside the .bin file is not an issue? Is that true?
Edit to add: I just realized this, and it should have probably been obvious: on the macro scale, there are two intermediary labels present in the disassembled code (addresses 0x8041932c and 0x80419340), which probably line up with labels "0:" (line 162) and "1:" (line 185), huh? So now I'm feeling pretty dang confident this is correct, and the fact that it lines up with the uimage header's stated entry address makes it seem like I have valid code on my hands!
-
One thing that took me a minute to realize as well is that instructions like PTR_LA, PTR_ADDU, PTR_SUBU, LONG_S, etc. are just macros defined mostly in kernel/arch/mips/include/asm/asm.h
-
Some real progress now!
To give you an idea of something you're looking for -- the goal is not to disassemble the entire kernel after all ;) -- check out this file: arch/mips/xburst/soc-x1000/chip-x1000/halley2/common/board_base.c. There's a function in it called board_base_init(). That's a function you want to locate, because it's the only place you can find a reference to platform_devices_array. Most platform_data structs which driver probe() functions will reference, are pointed to by this array, and from the platform_data struct you can learn GPIOs, details of the LCD interface configuration, clock speeds, and other useful stuff. I2C devices will probably be registered by board_base_init too. But some stuff will be hardcoded willy-nilly in the drivers. (I guess for a kernel you never update, maintainability is not a problem...)
Since board_base_init is an initcall it can be located in a table. See init/main.c, look for the initcall_levels array. And then look at the initcall_level_names below it. You can easily find the names array because it references a unique string "postcore". Once you find that you can find do_initcall_level() and from that, find initcall_levels. The initcalls are set up in one big table and the first entry of initcall_levels will point at the first one. You can trawl through the init calls one by one until you locate board_base_init(). That may require identifying a few other functions first, but the payoff is worth it because some information simply can't be found any other way.
I'm working through your example here, and I've identified - with a fair amount of certainty - the following:
- start_kernel()
- pr_notice()
- parse_args()
- "postcore" string
- initcall_level_names[]
- do_initcall_level()
- initcall_levels[]
- initcallx_start[] (0 through 7)
It took me a minute to figure out that you can not only bookmark stuff, but rename everything so the things you discover can propagate through the code.
It also took a bit of studying, but I think I see how items are added to the __initcall0_start[] arrays - through some magical define sorcery that took me like an hour to stare at, they use the labels "early", "core", "postcore", "arch", "subsys", "fs", "device", and "late" to correspond to levels 0-7, and then add functions by calling, for example, core_initcall(octeon_no_pci_init); to add octeon_no_pci_init() to the list of functions to run in level 1. Am I on track? Or off base? Or do I get a penalty for mixing my metaphors? :P
I still haven't identified board_base_init though...
-
you're on the right track. Another tip, I think I might've have discovered platform_device_add using this string: "%s.%d.auto". It's the only user of that string. platform_device_register is a small wrapper which calls platform_device_add, but it is hard to identify on its own. The latter function doesn't have many other callers though.
The chain of references should look something like this--
[initcall array] -> board_base_init() -> platform_device_register() -> platform_device_add()
knowing the two "ends" you just need to chase down the references and find board_base_init in the middle. BTW, the platform device array is an array of structs like struct { void* pdevice; void* pdata; int size; }. pdevice is a struct whose first member points to a name string so you can see what device you are dealing with, and pdata is a blob containing the useful info. And convieniently, size tells you how big the pdata struct is.
-
Well, I'm not sure if I'm barking up the wrong tree or what. I thought I had located board_base_init(), but none of the data it links to seem to quite be right. A bunch of pointers to strings are there, but between them are all zeroes (see screenshots).
I may need to backtrack and try to find something else, but I've done a lot of aimless scrolling in the hopes I would stumble across something, but that's probably futile.
-
One other thing I found that looks halfway interesting (see screenshot)
-
That's definitely the platform_devices_array at 80572854. Those extra references near the beginning that Ghidra adds are noise -- direct references that happen to occur from the loop. I guess its analysis is not very deep and it has a hard time understanding the difference between things added by the compiler to implement the loop guard condition vs. "genuine" data accesses... but whatever.
The pointers like PTR_s_gpio-keys_XXXX are pointing to struct platform_device instances, I think. That's a huge struct I believe, mainly members used at runtime, that's why there are so many zeros. But you should see the grey text like = 804b8c6c off to the side if there is any nonzero data inside which looks like an address, those addresses are clickable and you can follow the links in case they point to useful data or functions... I think there are tables of function pointers for driver ops. Maybe other things, I'm not sure.
DAT_80511180 for example should be one of the platform data blobs, in the case of jzmmc_v1.2 it will be struct jzmmc_platform_data. Cf. arch/mips/xburst/soc-x1000/chip-x1000/halley2/common/mmc.c. You should have another driver named something like jz-fb, and its platform data blob should define details of your LCD's interface. See struct jzfb_platform_data.
Go look at one of the other LCD drivers to get an idea of what you're looking for, mainly you want to figure out bpp, smart_config.bus_width, smart_config.write_gram_command, and smart_config.data_table. There are also the LCD ops for powering on the panel (GPIOs, levels, timings, etc. needed to power it up.)
FUN_805614c0 is most likely i2c_register_board_info. 1st argument is the bus number -- 0,1, or 2 corresponding to the same number bus on the X1000 -- and type of the 2nd argument is an array of struct i2c_board_info. That will contain at least your I2C device addresses, and possibly platform data blobs.
Another thing you can try is identifying gpiolib functions (drivers/gpio/gpiolib.c). Each function tends to have a WARN_ON statement or some other debug message which causes the functions to reference their own name. Most useful: gpiod_request and from there it is not hard to find the public API gpio_request. Often that is called with hardcoded GPIO numbers. And you can find the probe() functions of any drivers using a GPIO, since they all must call gpio_request before doing anything with the GPIO. gpio_direction_output/input are useful to find other driver functions. The GPIO numbering scheme is port*32 + pin. Ports are A = 0, B = 1, etc.
Those strings ldo0, ldo1, vdd_cpu... probably part of AXP192's axp_mfd driver. There's a copy of that driver in the M0 kernel sources (download links are on the Shanling Q1 wiki page). I did diassemble that driver quite a bit for the Q1, but I don't remember if I ever found the one voltage setting I needed to change in there, or if I just grabbed it from the running OF's sysfs. I think all I may have concluded from the diassembly was that the Q1 almost certainly had an AXP192, based on which #ifdef'd code appeared to be in the binary. There's a lot of data tables and boilerplate code, fairly straightforward but tedious to handle. Probably the interrupt line GPIO is the only easy-to-find thing.
-
Oh, excellent! I was thinking it wasn't it due to how it didn't seem to rigorously follow the struct pattern of pointer to string, pointer to data, size, but I guess I just didn't quite understand what I was looking for (data sure looks different when you're looking directly at bits and bytes, I guess)
Thanks for the explanations, I'll start digging and see what details I can find!
-
it didn't seem to rigorously follow the struct pattern of pointer to string, pointer to data, size
It does :) you just made some of those "pointer to data" an int. And ghidra obscured the beginning of the array with "false" references.
Recommend you create a struct -- select the data members of one struct instance in the main window, press shift+[ -- clear the types from the entire range (select it & press C) and assign the whole thing to an array of structs. (You may have to clear spurious types added by ghidra near the end of the array since it won't let you cause an overlap with other typed data.)
I found it's good to create structs aggressively even if creating them by hand is a bit slow... it pays off later. And ghidra also lets you use bitfields in structs, which is sometimes necessary. (bitfield type is named like uint:N). Should you need to amend the struct due to wrong members, wrong alignment, etc, you can quickly edit it and any place the struct is used will automatically update to the new definition. Very handy.
-
Go look at one of the other LCD drivers to get an idea of what you're looking for, mainly you want to figure out bpp, smart_config.bus_width, smart_config.write_gram_command, and smart_config.data_table. There are also the LCD ops for powering on the panel (GPIOs, levels, timings, etc. needed to power it up.)
Some progress on the LCD structs, though I'm having trouble figuring out jzfb_platform_data - nothing in it seems to line up quite right, even if I get creative with padding in between stuff. fb_videomode seemed to go together pretty smoothly though. This is pointed to from the pdata position.
jzfb_platform_data XREF[1]: 805728e8(*)
80514788 01 00 00 00 uint 1h
8051478c 14 48 51 80 addr fb_videomode =
80514790 00 ?? 00h
80514791 00 ?? 00h
80514792 00 ?? 00h
80514793 00 ?? 00h
80514794 0d ?? 0Dh
80514795 00 ?? 00h
80514796 00 ?? 00h
80514797 80 ?? 80h
80514798 18 ?? 18h
80514799 00 ?? 00h
8051479a 00 ?? 00h
8051479b 00 ?? 00h
8051479c 1f ?? 1Fh
8051479d 00 ?? 00h
8051479e 00 ?? 00h
8051479f 00 ?? 00h
805147a0 1f ?? 1Fh
805147a1 00 ?? 00h
805147a2 00 ?? 00h
805147a3 00 ?? 00h
805147a4 00 ?? 00h
805147a5 00 ?? 00h
805147a6 00 ?? 00h
805147a7 00 ?? 00h
805147a8 00 ?? 00h
805147a9 00 ?? 00h
805147aa 00 ?? 00h
805147ab 00 ?? 00h
805147ac 11 ?? 11h
805147ad 00 ?? 00h
805147ae 00 ?? 00h
805147af 00 ?? 00h
805147b0 01 ?? 01h
805147b1 00 ?? 00h
805147b2 00 ?? 00h
805147b3 00 ?? 00h
805147b4 4c 48 51 80 addr fb_videomode.flag = null
805147b8 08 ?? 08h
805147b9 00 ?? 00h
805147ba 00 ?? 00h
805147bb 00 ?? 00h
805147bc 00 ?? 00h
805147bd 00 ?? 00h
805147be 00 ?? 00h
805147bf 00 ?? 00h
805147c0 38 ?? 38h 8
805147c1 00 ?? 00h
805147c2 00 ?? 00h
805147c3 00 ?? 00h
805147c4 7c 48 51 80 addr LAB_8051487c
805147c8 00 ?? 00h
805147c9 00 ?? 00h
805147ca 00 ?? 00h
805147cb 00 ?? 00h
805147cc 00 ?? 00h
805147cd 00 ?? 00h
805147ce 00 ?? 00h
805147cf 00 ?? 00h
805147d0 00 ?? 00h
805147d1 00 ?? 00h
805147d2 00 ?? 00h
805147d3 00 ?? 00h
805147d4 00 ?? 00h
805147d5 00 ?? 00h
805147d6 00 ?? 00h
805147d7 00 ?? 00h
805147d8 00 ?? 00h
805147d9 00 ?? 00h
805147da 00 ?? 00h
805147db 00 ?? 00h
805147dc 00 ?? 00h
805147dd 00 ?? 00h
805147de 00 ?? 00h
805147df 00 ?? 00h
805147e0 00 ?? 00h
805147e1 00 ?? 00h
805147e2 00 ?? 00h
805147e3 00 ?? 00h
805147e4 00 ?? 00h
805147e5 00 ?? 00h
805147e6 00 ?? 00h
805147e7 00 ?? 00h
805147e8 00 ?? 00h
805147e9 00 ?? 00h
805147ea 00 ?? 00h
805147eb 00 ?? 00h
805147ec 00 ?? 00h
805147ed 00 ?? 00h
805147ee 00 ?? 00h
805147ef 00 ?? 00h
805147f0 a8 94 01 80 addr LAB_800194a8
805147f4 d4 93 01 80 addr FUN_800193d4
805147f8 00 ?? 00h
805147f9 00 ?? 00h
805147fa 00 ?? 00h
805147fb 00 ?? 00h
805147fc 00 ?? 00h
805147fd 00 ?? 00h
805147fe 00 ?? 00h
805147ff 00 ?? 00h
80514800 00 ?? 00h
80514801 00 ?? 00h
80514802 00 ?? 00h
80514803 00 ?? 00h
80514804 00 ?? 00h
80514805 00 ?? 00h
80514806 00 ?? 00h
80514807 00 ?? 00h
80514808 00 ?? 00h
80514809 00 ?? 00h
8051480a 00 ?? 00h
8051480b 00 ?? 00h
8051480c 00 ?? 00h
8051480d 00 ?? 00h
8051480e 00 ?? 00h
8051480f 00 ?? 00h
80514810 00 ?? 00h
80514811 00 ?? 00h
80514812 00 ?? 00h
80514813 00 ?? 00h
fb_videomode.flag XREF[1,1]: 8051478c(*), 805147b4(*)
fb_videomode
80514814 e0 8d 4b fb_video
80 50 00
00 00 40
80514814 e0 8d 4b 80 addr s_320x240_804b8de0 name = "320x240" XREF[1]: 8051478c(*)
80514818 50 00 00 00 uint 50h refresh
8051481c 40 01 00 00 uint 140h xres
80514820 f0 00 00 00 uint F0h yres
80514824 50 c3 00 00 uint C350h init_pixclock
80514828 00 00 00 00 uint 0h pixclock
8051482c 00 00 00 00 uint 0h left_margin
80514830 00 00 00 00 uint 0h right_margin
80514834 00 00 00 00 uint 0h upper_margin
80514838 00 00 00 00 uint 0h lower_margin
8051483c 00 00 00 00 uint 0h hsync_len
80514840 03 00 00 00 uint 3h vsync_len
80514844 00 00 00 00 uint 0h sync
80514848 00 00 00 00 uint 0h vmode
8051484c 2c 2c 2c 2c uint 2C2C2C2Ch flag XREF[1]: 805147b4(*)
I had some progress on jzmmc_platform_data, too, but again I can't quite get stuff to line up correctly. I think the mmc_recovery_info struct is located directly below it, but it doesn't seem to fit before the pointer to s_bt_power_804b761c.
jzmmc_platform_data_80511180 XREF[1]: 8057287c(*)
80511180 02 00 00 jzmmc_pl
00 00 00
30 00 07
80511180 02 00 ushort 2h removal XREF[1]: 8057287c(*)
80511182 00 00 ushort 0h sdio_clk
80511184 00 00 30 00 uint 300000h ocr_avail
80511188 07 00 00 00 uint 7h capacity
8051118c 00 00 00 00 uint 0h pm_flags
80511190 00 6c dc 02 uint 2DC6C00h max_freq
80511194 00 00 00 00 int 0h UNDEFINED
80511198 b0 11 51 80 00 addr PTR_805111b0 *recovery_info = NaP
00 00 00
805111a0 00 00 00 00 00 addr 00000000 *gpio
00 00 00
805111a8 00 00 00 00 uint 0h pio_mode
805111ac 00 00 00 00 int 0h (*private_in
PTR_805111b0 XREF[1]: 80511198(*)
805111b0 ff ff ff addr * NaP
ff 2b 00
00 00
partition_num
805111b8 2a 00 01 00 uint 1002Ah
permission
805111bc ff ff ff ff uint FFFFFFFFh
protect_boundary XREF[1]: 80572884(*)
805111c0 1c 76 4b 80 uint * s_bt_power_804b761c = "bt_power"
805111c4 ff ff ff ff int FFFFFFFFh
805111c8 00 ?? 00h
805111c9 00 ?? 00h
I'm going to just keep inspecting different stuff, there's a lot to go through!
-
I have a suspicion that the jzfb_platform_data struct is actually the one outlined in arch/mips/xburst/soc-x1000/include/mach/jzfb.h, could this be true?
-
I'm going out on a limb here, but I found 2 LCD jzfb_platform_data structs that seem to match up somewhat with the data I have, though I can't get the struct to line up right (it uses bitfields...), so I'm probably in error here...
lcd-bmtf200_2.c and lcd-ili9342_320240.c:
struct jzfb_platform_data jzfb_pdata = {
.num_modes = 1,
.modes = &jzfb0_videomode,
.lcd_type = LCD_TYPE_SLCD,
.bpp = 24,
.width = 31,
.height = 31,
.pinmd = 0,
.smart_config.rsply_cmd_high = 0,
.smart_config.csply_active_high = 0,
.smart_config.newcfg_fmt_conv = 1,
.smart_config.write_gram_cmd = cmd_buf,
.smart_config.clkply_active_rising = 1,
.smart_config.length_cmd = ARRAY_SIZE(cmd_buf),
.smart_config.bus_width = 8,
.smart_config.length_data_table = ARRAY_SIZE(ili9342_data_table),
.smart_config.data_table = ili9342_data_table,
.dither_enable = 0,
};
and my data struct - which is all messed up, but the first few items seem to make sense:
jzfb_platform_data XREF[1]: 805728e8(*)
80514788 01 00 00 jzfb_pla
00 14 48
51 80 00
80514788 01 00 00 00 long 1h num_modes XREF[1]: 805728e8(*)
8051478c 14 48 51 80 addr fb_videomode *modes =
80514790 00 00 00 00 addr 00000000 *dsi_data
80514794 0d 00 00 80 uint 8000000Dh lcd_type
80514798 18 00 00 00 uint 18h bpp
8051479c 1f 00 00 00 uint 1Fh width
805147a0 1f 00 00 00 uint 1Fh height
805147a4 00 uint:1 0h pinmd:1
805147a4 00 uint:1 0h pixclk_falli
805147a4 00 uint:1 0h data_enable_
805147a5 00 00 00 00 00 smart_co field_0x1d
00 00 11 00 00
00 01 00 00 00
805147a5 00 00 00 00 uint 0h smart_type
805147a9 00 uint:1 0h clkply_activ
805147a9 00 uint:1 0h rsply_cmd_hi
805147a9 00 uint:1 0h csply_active
805147a9 00 uint:1 0h newcfg_6800_
805147a9 00 uint:1 0h newcfg_fmt_c
805147a9 00 uint:1 0h datatx_type_
805147a9 00 uint:1 0h newcfg_cmd_9
805147aa 00 00 11 00 ulong 110000h length_cmd
805147ae 00 00 01 00 addr 00010000 write_gram_cmd
805147b2 00 00 4c 48 ulong 484C0000h bus_width
805147b6 51 80 08 00 uint 88051h data_times
805147ba 00 00 00 00 ulong 0h length_data_
805147be 00 00 38 00 addr 00380000 *data_table
805147c2 00 00 7c 48 addr 487c0000 (*init)
805147c6 51 80 00 00 addr 00008051 (*gpio_for_s
805147ca 00 00 00 00 int 0h te_gpio
805147ce 00 00 00 00 int 0h te_irq_level
805147d2 00 uint:1 0h dither_enabl
805147d3 00 00 00 00 00 dither dither
00 00 00 00 00
00 00
805147d3 00 00 00 00 uint 0h dither_red
805147d7 00 00 00 00 uint 0h dither_green
805147db 00 00 00 00 uint 0h dither_blue
805147df 00 ?? 00h
805147e0 00 ?? 00h
805147e1 00 ?? 00h
805147e2 00 ?? 00h
805147e3 00 ?? 00h
I dunno, I've been playing with it for an hour or 2 and I can't seem to get anything past "height" to make any sense.
-
0x2c2c2c2c is most definitely the value pointed to by smart_config.write_gram_cmd. 0x2c is the standard write_memory_start command of MIPI DCS, used to initiate writing data to the LCD controller's framebuffer RAM. It's not fb_videomode.flag.
In the M3K kernel sources (https://github.com/FiiOapp/FiiO_Kernel_M3K/blob/master/include/linux/fb.h#L762) the init_pixclock member is dropped vs. the YuanhuanLiang repo. That's probably why your fb_videomode struct is too big. Maybe you should check the M3K sources for other structs that seem "wrong", it's possible that the Eros Q is using sources similar to the M3K's. (A reminder that your source won't necessarily line up with the binary!)
So with this in mind, jzfb stuff looks lined up right to me:
805147ac = bitfield
clkply_active_rising=1
newcfg_fmt_conv=1
others=0
(this is standard fare, nothing exotic)
805147b8 = smart_config.bus_width
805147c0 = smart_config.length_data_table
805147c4 = smart_config.data_table
Also a look at this
805147a4 00 uint:1 0h pinmd:1
805147a4 00 uint:1 0h pixclk_falli
805147a4 00 uint:1 0h data_enable_
805147a5 00 00 00 00 00 smart_co field_0x1d
That start address 805147a5 is almost certainly wrong. The alignment of the struct is at least 4 bytes and struct alignment = maximum alignment of any member. Plus, the first member must be aligned wrong because enums seem to be ints under the ABI used (an implementation detail which I haven't bothered to verify but seems to be true).
Weirdly, Ghidra doesn't align structs by default -- in the lower right corner of the struct editor is an unobtrusive "Align" checkbox. Checking that will make Ghidra insert the correct padding to follow C alignment rules (afaict). It seems Ghidra defaults to "packed" alignment by default... it confused me for a couple hours too. :o
In this particular case I think I can say with certainty the alignment is wrong -- but in general, you would need to check the generated code to see if the compiler emitted code to access aligned addresses or unaligned addresses. The lw/sw instructions to load/store a word require 4 byte aligned addresses, whereas to access an improperly aligned word, the compiler would need to generate different instructions -- in the case of MIPS the instructions used would depend on the ISA version targeted, IIRC the older versions don't have any unaligned load/store instructions.
I have a suspicion that the jzfb_platform_data struct is actually the one outlined in arch/mips/xburst/soc-x1000/include/mach/jzfb.h, could this be true?
That's my assumption. Sure, it's defined in other parts of the xburst tree for other SoCs, but this is an X1000 kernel... where else would it be defined?
-
0x2c2c2c2c is most definitely the value pointed to by smart_config.write_gram_cmd. 0x2c is the standard write_memory_start command of MIPI DCS, used to initiate writing data to the LCD controller's framebuffer RAM. It's not fb_videomode.flag.
Aha, so it's cmd_buf[], I see it there now in my example driver setup.
In the M3K kernel sources (https://github.com/FiiOapp/FiiO_Kernel_M3K/blob/master/include/linux/fb.h#L762) the init_pixclock member is dropped vs. the YuanhuanLiang repo. That's probably why your fb_videomode struct is too big. Maybe you should check the M3K sources for other structs that seem "wrong", it's possible that the Eros Q is using sources similar to the M3K's. (A reminder that your source won't necessarily line up with the binary!)
I see! That did make it fit, and all the values look sane now. Bookmarking the m3k sources...
So with this in mind, jzfb stuff looks lined up right to me:
805147ac = bitfield
clkply_active_rising=1
newcfg_fmt_conv=1
others=0
(this is standard fare, nothing exotic)
805147b8 = smart_config.bus_width
805147c0 = smart_config.length_data_table
805147c4 = smart_config.data_table
Also a look at this
805147a4 00 uint:1 0h pinmd:1
805147a4 00 uint:1 0h pixclk_falli
805147a4 00 uint:1 0h data_enable_
805147a5 00 00 00 00 00 smart_co field_0x1d
That start address 805147a5 is almost certainly wrong. The alignment of the struct is at least 4 bytes and struct alignment = maximum alignment of any member. Plus, the first member must be aligned wrong because enums seem to be ints under the ABI used (an implementation detail which I haven't bothered to verify but seems to be true).
Weirdly, Ghidra doesn't align structs by default -- in the lower right corner of the struct editor is an unobtrusive "Align" checkbox. Checking that will make Ghidra insert the correct padding to follow C alignment rules (afaict). It seems Ghidra defaults to "packed" alignment by default... it confused me for a couple hours too. :o
In this particular case I think I can say with certainty the alignment is wrong -- but in general, you would need to check the generated code to see if the compiler emitted code to access aligned addresses or unaligned addresses. The lw/sw instructions to load/store a word require 4 byte aligned addresses, whereas to access an improperly aligned word, the compiler would need to generate different instructions -- in the case of MIPS the instructions used would depend on the ISA version targeted, IIRC the older versions don't have any unaligned load/store instructions.
The align checkbox... That magically fixed it! Yep, now that'll be the first thing I check.
That's my assumption. Sure, it's defined in other parts of the xburst tree for other SoCs, but this is an X1000 kernel... where else would it be defined?
Oh, I thought it was the jzfb_platform_data from drivers/video/jz_vfb/vfb.h at first, which is really short in comparison, but it's missing *dsi_pdata and lcd_type between *modes and bpp, so I was confused.
This is definitely a crash course in data structures, something I probably should have taken a class on in college... ;D
-
ah, didn't realize that other driver used the same name... it looks like an in-memory framebuffer with no hardware interface. The real LCD driver would be under drivers/video/jz_fb_v12/. Or maybe the v11 or v13 versions. I'm not sure why they put 3 versions in one source tree.
-
Do you think it's worth trying to construct the platform_device structure? It requires building the device struct which is a bit daunting. Plus, there are some #ifdefs that I'm not sure if this device would have or not. Just not sure if that's something worth going through or not.
Edit: the big one defined in kernel/include/linux/device.h
-
FWIW, I didn't ever figure out struct device and didn't try -- once I realized how complicated it was. But I did do some random clicking inside of them and it might've been useful once or twice... there's probably not much there which can't be found another way, because that struct is generic.
One tactic you could use to handle this kind of thing -- create the struct, but pad out the unknown parts with byte arrays. Choose the size of the byte arrays to make the known members line up properly, and if you find more information, you can always split up the byte arrays and add new members in the middle, etc.
So, maybe find a driver which you have source code for and which statically initializes a platform_device/device struct; locate the beginning of that struct in the binary, then go over the struct's members in order, stepping inside all nested structs along the way. Then, identify the initialized members and learn their offsets. Of course any initialized data that you aren't expecting will throw off the count, but with luck you can identify a few members. Then you can tentatively identify adjacent members via the source code as long as you don't have to pass over anything of unknown size.
-
FWIW, I didn't ever figure out struct device and didn't try -- once I realized how complicated it was. But I did do some random clicking inside of them and it might've been useful once or twice... there's probably not much there which can't be found another way, because that struct is generic.
One tactic you could use to handle this kind of thing -- create the struct, but pad out the unknown parts with byte arrays. Choose the size of the byte arrays to make the known members line up properly, and if you find more information, you can always split up the byte arrays and add new members in the middle, etc.
So, maybe find a driver which you have source code for and which statically initializes a platform_device/device struct; locate the beginning of that struct in the binary, then go over the struct's members in order, stepping inside all nested structs along the way. Then, identify the initialized members and learn their offsets. Of course any initialized data that you aren't expecting will throw off the count, but with luck you can identify a few members. Then you can tentatively identify adjacent members via the source code as long as you don't have to pass over anything of unknown size.
That seems like a sane way to do it. I'll have to give it a shot.
Meanwhile, I think I have some real, actual information about the LCD and backlight! well, maybe. I haven't quite figured out what the controller is, and I don't know what pins it uses to connect, but I think I've found quite a few details about it. I think all the values make sense, as far as I can tell - though I don't have much experience with these beyond what I've seen in example structs in the X1000 kernel source.
The platform_data struct:
jzfb_platform_data XREF[1]: 805728e8(*)
80514788 01 00 00 jzfb_pla
00 14 48
51 80 00
80514788 01 00 00 00 long 1h num_modes XREF[1]: 805728e8(*)
8051478c 14 48 51 80 addr fb_videomode *modes =
80514790 00 00 00 00 addr 00000000 *dsi_data
80514794 0d 00 00 80 jzfb_lcd LCD_TYPE_SLCD lcd_type
80514798 18 00 00 00 uint 18h bpp
8051479c 1f 00 00 00 uint 1Fh width
805147a0 1f 00 00 00 uint 1Fh height
805147a4 00 uint:1 0h pinmd:1
805147a4 00 uint:1 0h pixclk_falli
805147a4 00 00 00 00 uint:1 0h data_enable_
805147a8 00 00 00 00 11 smart_co field_0x20
00 00 00 01 00
00 00 4c 48 51
805147a8 00 00 00 00 smart_lc SMART_LCD_TYPE_PARALLEL smart_type
805147ac 11 uint:1 1h clkply_activ
805147ac 11 uint:1 0h rsply_cmd_hi
805147ac 11 uint:1 0h csply_active
805147ac 11 uint:1 0h newcfg_6800_
805147ac 11 uint:1 1h newcfg_fmt_c
805147ac 11 uint:1 0h datatx_type_
805147ac 11 00 00 00 uint:1 0h newcfg_cmd_9
805147b0 01 00 00 00 ulong 1h length_cmd
805147b4 4c 48 51 80 addr 8051484c write_gram_cmd
805147b8 08 00 00 00 ulong 8h bus_width
805147bc 00 00 00 00 uint 0h data_times
805147c0 38 00 00 00 ulong 38h length_data_
805147c4 7c 48 51 80 addr 8051487c *data_table
805147c8 00 00 00 00 addr 00000000 (*init)
805147cc 00 00 00 00 addr 00000000 (*gpio_for_s
805147d0 00 00 00 00 int 0h te_gpio
805147d4 00 00 00 00 int 0h te_irq_level
805147d8 00 00 00 00 00 uint:1 0h dither_enabl
00 00 00
805147e0 00 00 00 00 00 dither dither
00 00 00 00 00
00 00 00 00 00
805147e0 00 00 00 00 uint 0h dither_red
805147e4 00 00 00 00 uint 0h dither_green
805147e8 00 00 00 00 00 uint 0h dither_blue
00 00 00
805147f0 a8 94 01 80 d4 lcd_call lcd_callback
93 01 80 00 00
00 00 00 00 00
805147f0 a8 94 01 80 addr 800194a8 *lcd_initial
805147f4 d4 93 01 80 addr 800193d4 *lcd_initial
805147f8 00 00 00 00 addr 00000000 *lcd_power_o
805147fc 00 00 00 00 addr 00000000 *lcd_power_o
80514800 00 00 00 00 addr 00000000 *lcd_power_o
80514804 00 00 00 00 addr 00000000 *lcd_power_o
80514808 00 00 00 00 addr 00000000 *dma_transfe
8051480c 00 00 00 00 addr 00000000 *dma_transfe
80514810 00 ?? 00h
80514811 00 ?? 00h
80514812 00 ?? 00h
80514813 00 ?? 00h
The fb_videomode struct and cmd_buf[] array:
fb_videomode XREF[1]: 8051478c(*)
80514814 e0 8d 4b fb_video
80 50 00
00 00 40
80514814 e0 8d 4b 80 addr s_320x240_804b8de0 name = "320x240" XREF[1]: 8051478c(*)
80514818 50 00 00 00 uint 50h refresh
8051481c 40 01 00 00 uint 140h xres
80514820 f0 00 00 00 uint F0h yres
80514824 50 c3 00 00 uint C350h pixclock
80514828 00 00 00 00 uint 0h left_margin
8051482c 00 00 00 00 uint 0h right_margin
80514830 00 00 00 00 uint 0h upper_margin
80514834 00 00 00 00 uint 0h lower_margin
80514838 00 00 00 00 uint 0h hsync_len
8051483c 00 00 00 00 uint 0h vsync_len
80514840 03 00 00 00 uint 3h sync
80514844 00 00 00 00 uint 0h vmode
80514848 00 00 00 00 uint 0h flag
cmd_buf[]
8051484c 2c 2c 2c 2c ulong[1]
8051484c [0] 2C2C2C2Ch
And the command set; this seems to be really generic, as far as I've seen:
8051487c 00 00 00 smart_lc
00 c8 00
00 00 01
8051487c 00 00 00 00 c8 smart_lc [0]
00 00 00
8051487c 00 00 00 00 smart_co SMART_CONFIG_CMD type //Set EXTC
80514880 c8 00 00 00 uint C8h value
80514884 01 00 00 00 ff smart_lc [1]
00 00 00
80514884 01 00 00 00 smart_co SMART_CONFIG_DATA type
80514888 ff 00 00 00 uint FFh value
8051488c 01 00 00 00 93 smart_lc [2]
00 00 00
8051488c 01 00 00 00 smart_co SMART_CONFIG_DATA type
80514890 93 00 00 00 uint 93h value
80514894 01 00 00 00 42 smart_lc [3]
00 00 00
80514894 01 00 00 00 smart_co SMART_CONFIG_DATA type
80514898 42 00 00 00 uint 42h value
8051489c 00 00 00 00 36 smart_lc [4]
00 00 00
8051489c 00 00 00 00 smart_co SMART_CONFIG_CMD type //Memory Access Co
805148a0 36 00 00 00 uint 36h value
805148a4 01 00 00 00 d8 smart_lc [5]
00 00 00
805148a4 01 00 00 00 smart_co SMART_CONFIG_DATA type //MY,MX,MV,ML,BGR,MH
805148a8 d8 00 00 00 uint D8h value
805148ac 00 00 00 00 3a smart_lc [6]
00 00 00
805148ac 00 00 00 00 smart_co SMART_CONFIG_CMD type //Pixel Format Set
805148b0 3a 00 00 00 uint 3Ah value
805148b4 01 00 00 00 66 smart_lc [7]
00 00 00
805148b4 01 00 00 00 smart_co SMART_CONFIG_DATA type //DPI [2:0],DBI [2
805148b8 66 00 00 00 uint 66h value
805148bc 00 00 00 00 c0 smart_lc [8]
00 00 00
805148bc 00 00 00 00 smart_co SMART_CONFIG_CMD type //Power Control 1
805148c0 c0 00 00 00 uint C0h value
805148c4 01 00 00 00 15 smart_lc [9]
00 00 00
805148c4 01 00 00 00 smart_co SMART_CONFIG_DATA type //VRH[5:0]
805148c8 15 00 00 00 uint 15h value
805148cc 01 00 00 00 15 smart_lc [10]
00 00 00
805148cc 01 00 00 00 smart_co SMART_CONFIG_DATA type //VC[3:0]
805148d0 15 00 00 00 uint 15h value
805148d4 00 00 00 00 c1 smart_lc [11]
00 00 00
805148d4 00 00 00 00 smart_co SMART_CONFIG_CMD type //Power Control 2
805148d8 c1 00 00 00 uint C1h value
805148dc 01 00 00 00 01 smart_lc [12]
00 00 00
805148dc 01 00 00 00 smart_co SMART_CONFIG_DATA type //SAP[2:0],BT[3:0]
805148e0 01 00 00 00 uint 1h value
805148e4 00 00 00 00 c5 smart_lc [13]
00 00 00
805148e4 00 00 00 00 smart_co SMART_CONFIG_CMD type //VCOM
805148e8 c5 00 00 00 uint C5h value
805148ec 01 00 00 00 da smart_lc [14]
00 00 00
805148ec 01 00 00 00 smart_co SMART_CONFIG_DATA type
805148f0 da 00 00 00 uint DAh value
805148f4 00 00 00 00 b1 smart_lc [15]
00 00 00
805148f4 00 00 00 00 smart_co SMART_CONFIG_CMD type
805148f8 b1 00 00 00 uint B1h value
805148fc 01 00 00 00 00 smart_lc [16]
00 00 00
805148fc 01 00 00 00 smart_co SMART_CONFIG_DATA type
80514900 00 00 00 00 uint 0h value
80514904 01 00 00 00 1b smart_lc [17]
00 00 00
80514904 01 00 00 00 smart_co SMART_CONFIG_DATA type
80514908 1b 00 00 00 uint 1Bh value
8051490c 00 00 00 00 b4 smart_lc [18]
00 00 00
8051490c 00 00 00 00 smart_co SMART_CONFIG_CMD type
80514910 b4 00 00 00 uint B4h value
80514914 01 00 00 00 02 smart_lc [19]
00 00 00
80514914 01 00 00 00 smart_co SMART_CONFIG_DATA type
80514918 02 00 00 00 uint 2h value
8051491c 00 00 00 00 e0 smart_lc [20]
00 00 00
8051491c 00 00 00 00 smart_co SMART_CONFIG_CMD type
80514920 e0 00 00 00 uint E0h value
80514924 01 00 00 00 0f smart_lc [21]
00 00 00
80514924 01 00 00 00 smart_co SMART_CONFIG_DATA type //P01-VP63
80514928 0f 00 00 00 uint Fh value
8051492c 01 00 00 00 13 smart_lc [22]
00 00 00
8051492c 01 00 00 00 smart_co SMART_CONFIG_DATA type //P02-VP62
80514930 13 00 00 00 uint 13h value
80514934 01 00 00 00 17 smart_lc [23]
00 00 00
80514934 01 00 00 00 smart_co SMART_CONFIG_DATA type //P03-VP61
80514938 17 00 00 00 uint 17h value
8051493c 01 00 00 00 04 smart_lc [24]
00 00 00
8051493c 01 00 00 00 smart_co SMART_CONFIG_DATA type //P04-VP59
80514940 04 00 00 00 uint 4h value
80514944 01 00 00 00 13 smart_lc [25]
00 00 00
80514944 01 00 00 00 smart_co SMART_CONFIG_DATA type //P05-VP57
80514948 13 00 00 00 uint 13h value
8051494c 01 00 00 00 07 smart_lc [26]
00 00 00
8051494c 01 00 00 00 smart_co SMART_CONFIG_DATA type //P06-VP50
80514950 07 00 00 00 uint 7h value
80514954 01 00 00 00 40 smart_lc [27]
00 00 00
80514954 01 00 00 00 smart_co SMART_CONFIG_DATA type //P07-VP43
80514958 40 00 00 00 uint 40h value
8051495c 01 00 00 00 39 smart_lc [28]
00 00 00
8051495c 01 00 00 00 smart_co SMART_CONFIG_DATA type //P08-VP27,36
80514960 39 00 00 00 uint 39h value
80514964 01 00 00 00 4f smart_lc [29]
00 00 00
80514964 01 00 00 00 smart_co SMART_CONFIG_DATA type //P09-VP20
80514968 4f 00 00 00 uint 4Fh value
8051496c 01 00 00 00 06 smart_lc [30]
00 00 00
8051496c 01 00 00 00 smart_co SMART_CONFIG_DATA type //P10-VP13
80514970 06 00 00 00 uint 6h value
80514974 01 00 00 00 0d smart_lc [31]
00 00 00
80514974 01 00 00 00 smart_co SMART_CONFIG_DATA type //P11-VP6
80514978 0d 00 00 00 uint Dh value
8051497c 01 00 00 00 0a smart_lc [32]
00 00 00
8051497c 01 00 00 00 smart_co SMART_CONFIG_DATA type //P12-VP4
80514980 0a 00 00 00 uint Ah value
80514984 01 00 00 00 1f smart_lc [33]
00 00 00
80514984 01 00 00 00 smart_co SMART_CONFIG_DATA type //P13-VP2
80514988 1f 00 00 00 uint 1Fh value
8051498c 01 00 00 00 22 smart_lc [34]
00 00 00
8051498c 01 00 00 00 smart_co SMART_CONFIG_DATA type //P14-VP1
80514990 22 00 00 00 uint 22h value
80514994 01 00 00 00 00 smart_lc [35]
00 00 00
80514994 01 00 00 00 smart_co SMART_CONFIG_DATA type //P15-VP0
80514998 00 00 00 00 uint 0h value
8051499c 00 00 00 00 e1 smart_lc [36]
00 00 00
8051499c 00 00 00 00 smart_co SMART_CONFIG_CMD type
805149a0 e1 00 00 00 uint E1h value
805149a4 01 00 00 00 00 smart_lc [37]
00 00 00
805149a4 01 00 00 00 smart_co SMART_CONFIG_DATA type //P01
805149a8 00 00 00 00 uint 0h value
805149ac 01 00 00 00 21 smart_lc [38]
00 00 00
805149ac 01 00 00 00 smart_co SMART_CONFIG_DATA type //P02
805149b0 21 00 00 00 uint 21h value
805149b4 01 00 00 00 24 smart_lc [39]
00 00 00
805149b4 01 00 00 00 smart_co SMART_CONFIG_DATA type //P03
805149b8 24 00 00 00 uint 24h value
805149bc 01 00 00 00 03 smart_lc [40]
00 00 00
805149bc 01 00 00 00 smart_co SMART_CONFIG_DATA type //P04
805149c0 03 00 00 00 uint 3h value
805149c4 01 00 00 00 0f smart_lc [41]
00 00 00
805149c4 01 00 00 00 smart_co SMART_CONFIG_DATA type //P05
805149c8 0f 00 00 00 uint Fh value
805149cc 01 00 00 00 05 smart_lc [42]
00 00 00
805149cc 01 00 00 00 smart_co SMART_CONFIG_DATA type //P06
805149d0 05 00 00 00 uint 5h value
805149d4 01 00 00 00 38 smart_lc [43]
00 00 00
805149d4 01 00 00 00 smart_co SMART_CONFIG_DATA type //P07
805149d8 38 00 00 00 uint 38h value
805149dc 01 00 00 00 32 smart_lc [44]
00 00 00
805149dc 01 00 00 00 smart_co SMART_CONFIG_DATA type //P08
805149e0 32 00 00 00 uint 32h value
805149e4 01 00 00 00 49 smart_lc [45]
00 00 00
805149e4 01 00 00 00 smart_co SMART_CONFIG_DATA type //P09
805149e8 49 00 00 00 uint 49h value
805149ec 01 00 00 00 00 smart_lc [46]
00 00 00
805149ec 01 00 00 00 smart_co SMART_CONFIG_DATA type //P10
805149f0 00 00 00 00 uint 0h value
805149f4 01 00 00 00 09 smart_lc [47]
00 00 00
805149f4 01 00 00 00 smart_co SMART_CONFIG_DATA type //P11
805149f8 09 00 00 00 uint 9h value
805149fc 01 00 00 00 08 smart_lc [48]
00 00 00
805149fc 01 00 00 00 smart_co SMART_CONFIG_DATA type //P12
80514a00 08 00 00 00 uint 8h value
80514a04 01 00 00 00 32 smart_lc [49]
00 00 00
80514a04 01 00 00 00 smart_co SMART_CONFIG_DATA type //P13
80514a08 32 00 00 00 uint 32h value
80514a0c 01 00 00 00 35 smart_lc [50]
00 00 00
80514a0c 01 00 00 00 smart_co SMART_CONFIG_DATA type //P14
80514a10 35 00 00 00 uint 35h value
80514a14 01 00 00 00 0f smart_lc [51]
00 00 00
80514a14 01 00 00 00 smart_co SMART_CONFIG_DATA type //P15
80514a18 0f 00 00 00 uint Fh value
80514a1c 00 00 00 00 11 smart_lc [52]
00 00 00
80514a1c 00 00 00 00 smart_co SMART_CONFIG_CMD type //Exit Sleep
80514a20 11 00 00 00 uint 11h value
80514a24 02 00 00 00 c0 smart_lc [53]
d4 01 00
80514a24 02 00 00 00 smart_co SMART_CONFIG_UDELAY type
80514a28 c0 d4 01 00 uint 1D4C0h value
80514a2c 00 00 00 00 29 smart_lc [54]
00 00 00
80514a2c 00 00 00 00 smart_co SMART_CONFIG_CMD type //Display On
80514a30 29 00 00 00 uint 29h value
80514a34 02 00 00 00 20 smart_lc [55]
4e 00 00
80514a34 02 00 00 00 smart_co SMART_CONFIG_UDELAY type
80514a38 20 4e 00 00 uint 4E20h value
80514a3c 00 ?? 00h
80514a3d 00 ?? 00h
80514a3e 00 ?? 00h
80514a3f 00 ?? 00h
(backlight stuff in another post)
-
Backlight stuff:
platform_pwm_backlight_data:
backlight_data
80514850 00 00 00 platform
00 ff 00
00 00 78
80514850 00 00 00 00 int 0h pwm_id XREF[1]: 80514648(*)
80514854 ff 00 00 00 uint FFh max_brightness
80514858 78 00 00 00 uint 78h dft_brightness
8051485c 00 00 00 00 uint 0h lth_brightness
80514860 30 75 00 00 uint 7530h pwm_period_ns
80514864 00 00 00 00 addr 00000000 *levels
80514868 1c 94 01 80 addr backlight_init (*init)
8051486c b0 93 01 80 addr backlight_notify (*notify)
80514870 00 00 00 00 addr 00000000 (*notify_aft
80514874 b8 93 01 80 addr backlight_exit (*exit)
80514878 00 ?? 00h
80514879 00 ?? 00h
8051487a 00 ?? 00h
8051487b 00 ?? 00h
backlight_exit() - I'm fairly sure it's just a gpio_free() call:
void backlight_exit(void)
{
gpio_free(0x59);
return;
}
backlight_notify() - I think the fact this is empty is possibly just a good data point to ID the controller:
undefined4 backlight_notify(undefined4 param_1,undefined4 param_2)
{
return param_2;
}
and backlight_init() - I'm only like 75% sure the functions I've ID'd within backlight_init() are correct, so take it with a grain of salt and all:
int backlight_init(void)
{
int iVar1;
iVar1 = gpio_request(0x2e,s_lcd_power_804b8db4);
if (iVar1 == 0) {
FUN_801f92f0(0x2e,1);
iVar1 = gpio_request(0x2d,s_lcd_rst_804b8dc0);
if (iVar1 == 0) {
FUN_801f92f0(0x2d,1);
iVar1 = gpio_request(0x59,s_BL_PWR_804b8dc8);
if (iVar1 == 0) {
jz_gpio_set_func(0x59,0);
}
}
}
return iVar1;
}
-
I think I have might have the jzmmc_platform_data correctly set up here, or at least... it fits in the space:
jzmmc_platform_data_80511180 XREF[1]: 8057287c(*)
80511180 02 00 00 jzmmc_pl
00 00 00
30 00 07
80511180 02 00 ushort 2h removal XREF[1]: 8057287c(*)
80511182 00 00 ushort 0h sdio_clk
80511184 00 00 30 00 uint 300000h ocr_avail
80511188 07 00 00 00 uint 7h capacity
8051118c 00 00 00 00 uint 0h pm_flags
80511190 00 6c dc 02 uint 2DC6C00h max_freq
80511194 00 00 00 00 addr 00000000 *recovery_info
80511198 b0 11 51 80 addr card_gpio_805111b0 *gpio =
8051119c 00 00 00 00 uint 0h pio_mode
805111a0 00 00 00 00 int 0h (*private_in
805111a4 00 00 00 00 uint 0h type
805111a8 00 ?? 00h
805111a9 00 ?? 00h
805111aa 00 ?? 00h
805111ab 00 ?? 00h
805111ac 00 ?? 00h
805111ad 00 ?? 00h
805111ae 00 ?? 00h
805111af 00 ?? 00h
card_gpio_805111b0 XREF[1]: 80511198(*)
805111b0 ff ff ff card_gpio
ff 2b 00
00 00 2a
805111b0 ff ff ff ff jzmmc_pin wp XREF[1]: 80511198(*)
805111b0 ff ff short FFFFh num XREF[1]: 80511198(*)
805111b2 ff ff short FFFFh enable_level
805111b4 2b 00 00 00 jzmmc_pin cd
805111b4 2b 00 short 2Bh num
805111b6 00 00 short 0h enable_level
805111b8 2a 00 01 00 jzmmc_pin pwr
805111b8 2a 00 short 2Ah num
805111ba 01 00 short 1h enable_level
805111bc ff ff ff ff jzmmc_pin rst
805111bc ff ff short FFFFh num
805111be ff ff short FFFFh enable_level
per this enum
enum {
DONTCARE = 0,
NONREMOVABLE,
REMOVABLE,
MANUAL,
};
the removable value of 2h makes sense.
-
All that stuff you've got looks sensible and usable. LCD command set looks like MIPI standard so you just copy the initialization commands into rockbox and for sleep mode you can use sleep in/out commands like the M3K does. Get a look at those two lcd_initial functions too:
805147f0 a8 94 01 80 d4 lcd_call lcd_callback
93 01 80 00 00
00 00 00 00 00
805147f0 a8 94 01 80 addr 800194a8 *lcd_initial
805147f4 d4 93 01 80 addr 800193d4 *lcd_initial
that may be where you find the power up sequence. Though it seems the backlight code is doing a bit of it too.
FYI, I pulled up the Q1 kernel and the GPIO key data is pointed to from inside the device struct. In the Q1's case the pointer is 104 bytes from the start of the struct, and as it happens the key table got placed right at the end of the device struct. Worth a shot checking if it's the same in your kernel. Here's mine:
struct gpio_keys_button_ARRAY_806d0c48 XREF[1]: 806d0cf8(*)
806d0c48 67 00 00 struct g
00 35 00
00 00 00
806d0c48 67 00 00 00 35 struct g [0] XREF[1]: 806d0cf8(*)
00 00 00 00 00
00 00 54 bf 63
806d0c48 67 00 00 00 uint 67h code XREF[1]: 806d0cf8(*)
806d0c4c 35 00 00 00 int 35h gpio
806d0c50 00 00 00 00 int 0h active_low
806d0c54 54 bf 63 80 char * s_prev_key_8063bf54 desc = "prev key"
806d0c58 00 00 00 00 uint 0h type
806d0c5c 00 00 00 00 int 0h wakeup
806d0c60 0a 00 00 00 int Ah wakeup_event
806d0c64 00 00 00 00 int 0h debounce_int
806d0c68 00 00 00 00 bool FALSE can_disable
806d0c6c 00 00 00 00 int 0h value
806d0c70 00 00 00 00 uint 0h irq
806d0c74 6c 00 00 00 36 struct g [1]
00 00 00 00 00
00 00 60 bf 63
806d0c74 6c 00 00 00 uint 6Ch code
806d0c78 36 00 00 00 int 36h gpio
806d0c7c 00 00 00 00 int 0h active_low
806d0c80 60 bf 63 80 char * s_next_key_8063bf60 desc = "next key"
806d0c84 00 00 00 00 uint 0h type
806d0c88 00 00 00 00 int 0h wakeup
806d0c8c 0a 00 00 00 int Ah wakeup_event
806d0c90 00 00 00 00 int 0h debounce_int
806d0c94 00 00 00 00 bool FALSE can_disable
806d0c98 00 00 00 00 int 0h value
806d0c9c 00 00 00 00 uint 0h irq
806d0ca0 8b 00 00 00 3c struct g [2]
00 00 00 00 00
00 00 6c bf 63
806d0ca0 8b 00 00 00 uint 8Bh code
806d0ca4 3c 00 00 00 int 3Ch gpio
806d0ca8 00 00 00 00 int 0h active_low
806d0cac 6c bf 63 80 char * s_menu_key_8063bf6c desc = "menu key"
806d0cb0 00 00 00 00 uint 0h type
806d0cb4 00 00 00 00 int 0h wakeup
806d0cb8 0a 00 00 00 int Ah wakeup_event
806d0cbc 00 00 00 00 int 0h debounce_int
806d0cc0 00 00 00 00 bool FALSE can_disable
806d0cc4 00 00 00 00 int 0h value
806d0cc8 00 00 00 00 uint 0h irq
806d0ccc 74 00 00 00 3f struct g [3]
00 00 00 00 00
00 00 78 bf 63
806d0ccc 74 00 00 00 uint 74h code
806d0cd0 3f 00 00 00 int 3Fh gpio
806d0cd4 00 00 00 00 int 0h active_low
806d0cd8 78 bf 63 80 char * s_power_key_8063bf78 desc = "power key"
806d0cdc 00 00 00 00 uint 0h type
806d0ce0 01 00 00 00 int 1h wakeup
806d0ce4 0a 00 00 00 int Ah wakeup_event
806d0ce8 00 00 00 00 int 0h debounce_int
806d0cec 00 00 00 00 bool FALSE can_disable
806d0cf0 00 00 00 00 int 0h value
806d0cf4 00 00 00 00 uint 0h irq
806d0cf8 48 0c 6d struct g /* <---- !!! this is what the device struct points to */
80 04 00
00 00 00
806d0cf8 48 0c 6d 80 struct g struct gpio_keys_butto buttons =
806d0cfc 04 00 00 00 int 4h nbuttons
806d0d00 00 00 00 00 uint 0h poll_interval
806d0d04 01 00 00 00 uint:1 1h rep
806d0d08 00 00 00 00 addr 00000000 enable
806d0d0c 00 00 00 00 addr 00000000 disable
806d0d10 00 00 00 00 char * 00000000 name
-
All that stuff you've got looks sensible and usable. LCD command set looks like MIPI standard so you just copy the initialization commands into rockbox and for sleep mode you can use sleep in/out commands like the M3K does. Get a look at those two lcd_initial functions too:
805147f0 a8 94 01 80 d4 lcd_call lcd_callback
93 01 80 00 00
00 00 00 00 00
805147f0 a8 94 01 80 addr 800194a8 *lcd_initial
805147f4 d4 93 01 80 addr 800193d4 *lcd_initial
that may be where you find the power up sequence. Though it seems the backlight code is doing a bit of it too.
Good to hear it looks like I've got good code so far. Here's lcd_initialize_begin():
undefined4 lcd_initialize_begin(void)
{
int iVar1;
if (_DAT_805a2290 == 0) {
gpio_direction_output(0x2e,1);
iVar1 = 0x14;
while (iVar1 != 0) {
FUN_801e69e0(1000);
iVar1 = iVar1 + -1;
}
gpio_direction_output(0x2d,1);
iVar1 = 0xb;
while (iVar1 = iVar1 + -1, iVar1 != 0) {
FUN_801e69e0(1000);
}
_DAT_805a2290 = 1;
return 0;
}
return 0;
}
And lcd_initialize_end():
undefined4 lcd_initialize_end(void)
{
if (_DAT_805a2290 != 0) {
gpio_direction_output(0x2e,0);
gpio_direction_output(0x2d,0);
_DAT_805a2290 = 0;
}
return 0;
}
I don't quite follow why there is an initialize_begin and initialize_end, it looks like they're just setting pins 0x2e and 0x2d high, and then turning around and setting them low again. Do you know when the _begin and _end functions are run?
I did a quick look to see if I could find an example function to try and figure out what FUN_801e69e0() is, but no luck so far. I suspect it's just a delay function?
FYI, I pulled up the Q1 kernel and the GPIO key data is pointed to from inside the device struct. In the Q1's case the pointer is 104 bytes from the start of the struct, and as it happens the key table got placed right at the end of the device struct. Worth a shot checking if it's the same in your kernel. Here's mine:
struct gpio_keys_button_ARRAY_806d0c48 XREF[1]: 806d0cf8(*)
806d0c48 67 00 00 struct g
00 35 00
00 00 00
806d0c48 67 00 00 00 35 struct g [0] XREF[1]: 806d0cf8(*)
00 00 00 00 00
00 00 54 bf 63
806d0c48 67 00 00 00 uint 67h code XREF[1]: 806d0cf8(*)
806d0c4c 35 00 00 00 int 35h gpio
806d0c50 00 00 00 00 int 0h active_low
806d0c54 54 bf 63 80 char * s_prev_key_8063bf54 desc = "prev key"
806d0c58 00 00 00 00 uint 0h type
806d0c5c 00 00 00 00 int 0h wakeup
806d0c60 0a 00 00 00 int Ah wakeup_event
806d0c64 00 00 00 00 int 0h debounce_int
806d0c68 00 00 00 00 bool FALSE can_disable
806d0c6c 00 00 00 00 int 0h value
806d0c70 00 00 00 00 uint 0h irq
806d0c74 6c 00 00 00 36 struct g [1]
00 00 00 00 00
00 00 60 bf 63
806d0c74 6c 00 00 00 uint 6Ch code
806d0c78 36 00 00 00 int 36h gpio
806d0c7c 00 00 00 00 int 0h active_low
806d0c80 60 bf 63 80 char * s_next_key_8063bf60 desc = "next key"
806d0c84 00 00 00 00 uint 0h type
806d0c88 00 00 00 00 int 0h wakeup
806d0c8c 0a 00 00 00 int Ah wakeup_event
806d0c90 00 00 00 00 int 0h debounce_int
806d0c94 00 00 00 00 bool FALSE can_disable
806d0c98 00 00 00 00 int 0h value
806d0c9c 00 00 00 00 uint 0h irq
806d0ca0 8b 00 00 00 3c struct g [2]
00 00 00 00 00
00 00 6c bf 63
806d0ca0 8b 00 00 00 uint 8Bh code
806d0ca4 3c 00 00 00 int 3Ch gpio
806d0ca8 00 00 00 00 int 0h active_low
806d0cac 6c bf 63 80 char * s_menu_key_8063bf6c desc = "menu key"
806d0cb0 00 00 00 00 uint 0h type
806d0cb4 00 00 00 00 int 0h wakeup
806d0cb8 0a 00 00 00 int Ah wakeup_event
806d0cbc 00 00 00 00 int 0h debounce_int
806d0cc0 00 00 00 00 bool FALSE can_disable
806d0cc4 00 00 00 00 int 0h value
806d0cc8 00 00 00 00 uint 0h irq
806d0ccc 74 00 00 00 3f struct g [3]
00 00 00 00 00
00 00 78 bf 63
806d0ccc 74 00 00 00 uint 74h code
806d0cd0 3f 00 00 00 int 3Fh gpio
806d0cd4 00 00 00 00 int 0h active_low
806d0cd8 78 bf 63 80 char * s_power_key_8063bf78 desc = "power key"
806d0cdc 00 00 00 00 uint 0h type
806d0ce0 01 00 00 00 int 1h wakeup
806d0ce4 0a 00 00 00 int Ah wakeup_event
806d0ce8 00 00 00 00 int 0h debounce_int
806d0cec 00 00 00 00 bool FALSE can_disable
806d0cf0 00 00 00 00 int 0h value
806d0cf4 00 00 00 00 uint 0h irq
806d0cf8 48 0c 6d struct g /* <---- !!! this is what the device struct points to */
80 04 00
00 00 00
806d0cf8 48 0c 6d 80 struct g struct gpio_keys_butto buttons =
806d0cfc 04 00 00 00 int 4h nbuttons
806d0d00 00 00 00 00 uint 0h poll_interval
806d0d04 01 00 00 00 uint:1 1h rep
806d0d08 00 00 00 00 addr 00000000 enable
806d0d0c 00 00 00 00 addr 00000000 disable
806d0d10 00 00 00 00 char * 00000000 name
It sure does look like I have the same thing here:
8050ff40 00 ?? 00h
8050ff41 00 ?? 00h
8050ff42 00 ?? 00h
8050ff43 00 ?? 00h
8050ff44 00 ?? 00h
8050ff45 00 ?? 00h
8050ff46 00 ?? 00h
8050ff47 00 ?? 00h
gpio_keys_struct_ARRAY_8050ff48[4].active_low XREF[1,69]: 805100a8(*),
gpio_keys_struct_ARRAY_8050ff48 FUN_80063548:8006354c(*),
FUN_8008aeb0:8008aee4(*),
FUN_800a0864:800a0c6c(*),
FUN_800b20b8:800b1e04(*),
FUN_800b20b8:800b1e08(*),
FUN_800b20b8:800b1e10(*),
FUN_801c1c40:801c1c5c(*),
FUN_8021ad04:8021ad1c(*),
FUN_8021ad2c:8021ad44(*),
FUN_803488b8:8034895c(*),
FUN_80373478:80373478(*),
FUN_80373498:80373498(*),
FUN_80392634:80392664(*),
FUN_803c9a90:803c9ab0(*),
FUN_803d3900:803d3908(*),
FUN_80551ef4:80551f38(*),
FUN_80551ef4:805521b4(*),
FUN_8056a2fc:8056a340(*),
FUN_8056bed4:8056bef8(*)
8050ff48 a4 00 00 gpio_key
00 10 00
00 00 01
8050ff48 a4 00 00 00 10 gpio_key [0] XREF[1]: 805100a8(*)
00 00 00 01 00
00 00 e4 73 4b
8050ff48 a4 00 00 00 uint A4h code XREF[1]: 805100a8(*)
8050ff4c 10 00 00 00 int 10h gpio
8050ff50 01 00 00 00 int 1h active_low
8050ff54 e4 73 4b 80 char * s_play_key_804b73e4 desc = "play key"
8050ff58 01 00 00 00 uint 1h type
8050ff5c 00 00 00 00 int 0h wakeup
8050ff60 00 00 00 00 int 0h wakeup_event
8050ff64 00 00 00 00 int 0h debounce_int
8050ff68 00 00 00 00 bool FALSE can_disable
8050ff6c 00 00 00 00 int 0h value
8050ff70 00 00 00 00 uint 0h irq
8050ff74 8b 00 00 00 3c gpio_key [1]
00 00 00 01 00
00 00 f0 73 4b
8050ff74 8b 00 00 00 uint 8Bh code
8050ff78 3c 00 00 00 int 3Ch gpio
8050ff7c 01 00 00 00 int 1h active_low
8050ff80 f0 73 4b 80 char * s_menu_key_804b73f0 desc = "menu key"
8050ff84 01 00 00 00 uint 1h type
8050ff88 00 00 00 00 int 0h wakeup
8050ff8c 00 00 00 00 int 0h wakeup_event
8050ff90 00 00 00 00 int 0h debounce_int
8050ff94 00 00 00 00 bool FALSE can_disable
8050ff98 00 00 00 00 int 0h value
8050ff9c 00 00 00 00 uint 0h irq
8050ffa0 9e 00 00 00 65 gpio_key [2]
00 00 00 01 00
00 00 fc 73 4b
8050ffa0 9e 00 00 00 uint 9Eh code
8050ffa4 65 00 00 00 int 65h gpio
8050ffa8 01 00 00 00 int 1h active_low
8050ffac fc 73 4b 80 char * s_back_key_804b73fc desc = "back key"
8050ffb0 01 00 00 00 uint 1h type
8050ffb4 00 00 00 00 int 0h wakeup
8050ffb8 00 00 00 00 int 0h wakeup_event
8050ffbc 00 00 00 00 int 0h debounce_int
8050ffc0 00 00 00 00 bool FALSE can_disable
8050ffc4 00 00 00 00 int 0h value
8050ffc8 00 00 00 00 uint 0h irq
8050ffcc a3 00 00 00 64 gpio_key [3]
00 00 00 01 00
00 00 08 74 4b
8050ffcc a3 00 00 00 uint A3h code
8050ffd0 64 00 00 00 int 64h gpio
8050ffd4 01 00 00 00 int 1h active_low
8050ffd8 08 74 4b 80 char * s_next_key_804b7408 desc = "next key"
8050ffdc 01 00 00 00 uint 1h type
8050ffe0 00 00 00 00 int 0h wakeup
8050ffe4 0a 00 00 00 int Ah wakeup_event
8050ffe8 00 00 00 00 int 0h debounce_int
8050ffec 00 00 00 00 bool FALSE can_disable
8050fff0 00 00 00 00 int 0h value
8050fff4 00 00 00 00 uint 0h irq
8050fff8 a5 00 00 00 58 gpio_key [4] XREF[0,69]: FUN_800289b4:80028dec(*),
00 00 00 01 00 FUN_80063548:8006354c(*),
00 00 14 74 4b FUN_8008aeb0:8008aee4(*),
FUN_800a0864:800a0c6c(*),
FUN_800b20b8:800b1e04(*),
FUN_800b20b8:800b1e08(*),
FUN_800b20b8:800b1e10(*),
FUN_801c1c40:801c1c5c(*),
FUN_8021ad04:8021ad1c(*),
FUN_8021ad2c:8021ad44(*),
FUN_803488b8:8034895c(*),
FUN_80373478:80373478(*),
FUN_80373498:80373498(*),
FUN_80392634:80392664(*),
FUN_803c9a90:803c9ab0(*),
FUN_803d3900:803d3908(*),
FUN_80551ef4:80551f38(*),
FUN_80551ef4:805521b4(*),
FUN_8056a2fc:8056a340(*),
FUN_8056bed4:8056bef8(*)
8050fff8 a5 00 00 00 uint A5h code
8050fffc 58 00 00 00 int 58h gpio
80510000 01 00 00 00 int 1h active_low XREF[69]: FUN_800289b4:80028dec(*),
FUN_80063548:8006354c(*),
FUN_8008aeb0:8008aee4(*),
FUN_800a0864:800a0c6c(*),
FUN_800b20b8:800b1e04(*),
FUN_800b20b8:800b1e08(*),
FUN_800b20b8:800b1e10(*),
FUN_801c1c40:801c1c5c(*),
FUN_8021ad04:8021ad1c(*),
FUN_8021ad2c:8021ad44(*),
FUN_803488b8:8034895c(*),
FUN_80373478:80373478(*),
FUN_80373498:80373498(*),
FUN_80392634:80392664(*),
FUN_803c9a90:803c9ab0(*),
FUN_803d3900:803d3908(*),
FUN_80551ef4:80551f38(*),
FUN_80551ef4:805521b4(*),
FUN_80558aa8:80558b3c(*),
FUN_8056a2fc:8056a340(*), [more]
80510004 14 74 4b 80 char * s_prev_key_804b7414 desc = "prev key"
80510008 01 00 00 00 uint 1h type
8051000c 00 00 00 00 int 0h wakeup
80510010 0a 00 00 00 int Ah wakeup_event
80510014 00 00 00 00 int 0h debounce_int
80510018 00 00 00 00 bool FALSE can_disable
8051001c 00 00 00 00 int 0h value
80510020 00 00 00 00 uint 0h irq
80510024 72 00 00 00 13 gpio_key [5]
00 00 00 01 00
00 00 20 74 4b
80510024 72 00 00 00 uint 72h code
80510028 13 00 00 00 int 13h gpio
8051002c 01 00 00 00 int 1h active_low
80510030 20 74 4b 80 char * s_volum_down_key_804b7 desc = "volum down key"
80510034 01 00 00 00 uint 1h type
80510038 00 00 00 00 int 0h wakeup
8051003c 00 00 00 00 int 0h wakeup_event
80510040 00 00 00 00 int 0h debounce_int
80510044 00 00 00 00 bool FALSE can_disable
80510048 00 00 00 00 int 0h value
8051004c 00 00 00 00 uint 0h irq
80510050 73 00 00 00 11 gpio_key [6]
00 00 00 01 00
00 00 30 74 4b
80510050 73 00 00 00 uint 73h code
80510054 11 00 00 00 int 11h gpio
80510058 01 00 00 00 int 1h active_low
8051005c 30 74 4b 80 char * s_volum_up_key_804b7430 desc = "volum up key"
80510060 01 00 00 00 uint 1h type
80510064 00 00 00 00 int 0h wakeup
80510068 00 00 00 00 int 0h wakeup_event
8051006c 00 00 00 00 int 0h debounce_int
80510070 00 00 00 00 bool FALSE can_disable
80510074 00 00 00 00 int 0h value
80510078 00 00 00 00 uint 0h irq
8051007c 74 00 00 00 27 gpio_key [7]
00 00 00 01 00
00 00 40 74 4b
8051007c 74 00 00 00 uint 74h code
80510080 27 00 00 00 int 27h gpio
80510084 01 00 00 00 int 1h active_low
80510088 40 74 4b 80 char * s_backlight_key_804b7440 desc = "backlight key"
8051008c 00 00 00 00 uint 0h type
80510090 01 00 00 00 int 1h wakeup
80510094 00 00 00 00 int 0h wakeup_event
80510098 00 00 00 00 int 0h debounce_int
8051009c 00 00 00 00 bool FALSE can_disable
805100a0 00 00 00 00 int 0h value
805100a4 00 00 00 00 uint 0h irq
nbuttons (805100a8+4) XREF[1]: 8050fe08(*)
poll_interval (805100a8+8)
gpio_summary_struct_805100a8
805100a8 48 ff 50 gpio_sum
80 08 00
00 00 00
805100a8 48 ff 50 80 addr gpio_keys_struct_ARRAY field_0x0 = XREF[1]: 8050fe08(*)
805100ac 08 00 00 00 int 8h nbuttons
805100b0 00 00 00 00 uint 0h poll_interval
805100b4 00 00 00 00 uint:1 0h rep
805100b8 00 00 00 00 addr 00000000 enable
805100bc 00 00 00 00 addr 00000000 disable
805100c0 00 00 00 00 char * 00000000 name
805100c4 00 ?? 00h
805100c5 00 ?? 00h
805100c6 00 ?? 00h
805100c7 00 ?? 00h
805100c8 00 ?? 00h
805100c9 00 ?? 00h
805100ca 00 ?? 00h
805100cb 00 ?? 00h
I also found some miscellaneous gpio values by searching for the known gpio functions, but that can go in another post (our power went out a little bit ago, don't want to lose my progress...)
-
In the same vein, here's some ring key structs built from arch/mips/xburst/soc-x1000/chip-x1000/idriver/common/sa_keypad_ring.h. A lot (not all) of this idriver stuff seems to be applicable:
ring_keys_platform_data_8050fd38 XREF[1]: 8050f830(*)
8050fd38 68 fd 50 ring_key
80 02 00
00 00 00
8050fd38 68 fd 50 80 addr ring_keys_button_8050f *buttons = XREF[1]: 8050f830(*)
8050fd3c 02 00 00 00 int 2h nbuttons
8050fd40 00 00 00 00 uint 0h poll_interval
8050fd44 00 00 00 00 uint:1 0h rep:1
8050fd48 64 00 00 00 uint 64h timer_debounce
8050fd4c 37 00 00 00 int 37h ring1_gpio
8050fd50 38 00 00 00 int 38h ring2_gpio
8050fd54 00 00 00 00 addr 00000000 *enable
8050fd58 00 00 00 00 addr 00000000 *disable
8050fd5c 00 00 00 00 char * 00000000 *name
DAT_8050fd60 XREF[1]: 8050fd2c(*)
8050fd60 00 ?? 00h
8050fd61 00 ?? 00h
8050fd62 00 ?? 00h
8050fd63 00 ?? 00h
8050fd64 84 ?? 84h ? -> 804b7384
8050fd65 73 ?? 73h s
8050fd66 4b ?? 4Bh K
8050fd67 80 ?? 80h
ring_keys_button_8050fd68 XREF[1]: 8050fd38(*)
8050fd68 69 00 00 ring_key
00 c0 73
4b 80 02
8050fd68 69 00 00 00 uint 69h code XREF[1]: 8050fd38(*)
8050fd6c c0 73 4b 80 char * s_ring_left_804b73c0 *desc = "ring left"
8050fd70 02 00 00 00 int 2h direction
8050fd74 00 00 00 00 int 0h wakeup
8050fd78 00 00 00 00 bool FALSE can_disable
8050fd7c 6a 00 00 ring_key
00 cc 73
4b 80 01
8050fd7c 6a 00 00 00 uint 6Ah code
8050fd80 cc 73 4b 80 char * s_ring_right_804b73cc *desc = "ring right"
8050fd84 01 00 00 00 int 1h direction
8050fd88 00 00 00 00 int 0h wakeup
8050fd8c 00 00 00 00 bool FALSE can_disable
DAT_8050fd90 XREF[1]: FUN_802c2dd4:802c2fcc(*)
8050fd90 39 ?? 39h 9
8050fd91 00 ?? 00h
8050fd92 01 ?? 01h
8050fd93 00 ?? 00h
-
In no particular order, here's a small assortment of things I think I've ID'd:
Bluetooth Power Init:
int power_init(void)
{
int iVar1;
iVar1 = gpio_request(0x53,s_bt_reg_on_804b7610);
if (iVar1 == 0) {
gpio_direction_output(0x53,0);
}
return iVar1;
}
RTC init (though I imagine it's identical to the m3k and q1:
void rtc32k_init(void)
{
int iVar1;
_DAT_805a2280 = 0;
iVar1 = gpio_request(0x3a,s_rtc32k_804b8ce0);
if (iVar1 == 0) {
_DAT_805a2284 = 1;
}
else {
pr_notice(s_[Error]_request_rtc32k_gpio_fail_804b8ce8);
}
return;
}
snd_[something]_probe():
int snd_x_probe(int param_1)
{
int iVar1;
DAT_805454c0 = param_1 + 0x10;
iVar1 = FUN_8034e318();
if (iVar1 != 0) {
dev_err(param_1 + 0x10,s_snd_soc_register_card_failed_%d_804fb778,iVar1);
}
iVar1 = gpio_request(0x28,s_HP_Mute_804fb79c);
if (iVar1 == 0) {
gpio_direction_output(0x28,0);
}
iVar1 = gpio_request(0x29,s_LO_Power_804fb7a4);
if (iVar1 == 0) {
gpio_direction_output(0x29,0);
}
iVar1 = gpio_request(0x2c,s_DAC_Mute_804fb7b0);
if (iVar1 == 0) {
gpio_direction_output(0x2c,0);
}
iVar1 = gpio_request(0x26,s_Analog_power_804fb7bc);
if (iVar1 == 0) {
gpio_direction_output(0x26,0);
}
iVar1 = gpio_request(0x25,s_out_sel_804fb7cc);
if (iVar1 == 0) {
gpio_direction_output(0x25,0);
}
iVar1 = gpio_request(0x2f,s_out_mute_804fb7d4);
if (iVar1 == 0) {
gpio_direction_output(0x2f,1);
}
iVar1 = gpio_request(0x35,s_spdif_en_804fb7e0);
if (iVar1 == 0) {
gpio_direction_output(0x35,0);
}
return iVar1;
}
And the fact that apparently we have jzmmc_v1.2:
s_jzmmc_v1.2_804b8c6c XREF[1]: 80513d10(*)
804b8c6c 6a 7a 6d ds "jzmmc_v1.2"
6d 63 5f
76 31 2e
-
Your snd_x_probe has got to be the DAC/amp driver. But you probably guessed that already :).
The "ring keys" confused me... but I looked up a review and it seems this player uses a rotating wheel -- right? -- so that'd be a rotary encoder. You've got all button GPIOs. The menu key is the one you hold to enter USB boot mode (due to it being on GPIO B28, see x1000 manual secs 27.1 and 19.3.2). The LCD looks straightforward. The current Rockbox driver should work, although I'm not 100% certain how the 24 bit depth is handled because I never tested it. That might require some trial and error if it doesn't work the first try, since the x1000 manual is next to useless when it comes to pixel formats, what gets sent on the bus and why.
By the way, lcd_initialize_begin/end get called to power the panel on and off. Not sure exactly where and when they are called but their purpose seems clear enough.
I think you have pretty much everything you need to do a port. The pin labels seem clear enough that you can just guess where they go just based on the chip datasheets. Well, except for spdif_en. I can't make heads or tails of that. There's nothing SPDIF-related on this player, is there?
Oh, and did you find out the i2c bus layout yet? it's in board_base_init, those two calls near the end. What i2c chips are you expecting anyway? AXP192 is there, so aside from that is it just the DAC?
-
Your snd_x_probe has got to be the DAC/amp driver. But you probably guessed that already :).
The "ring keys" confused me... but I looked up a review and it seems this player uses a rotating wheel -- right? -- so that'd be a rotary encoder. You've got all button GPIOs. The menu key is the one you hold to enter USB boot mode (due to it being on GPIO B28, see x1000 manual secs 27.1 and 19.3.2). The LCD looks straightforward. The current Rockbox driver should work, although I'm not 100% certain how the 24 bit depth is handled because I never tested it. That might require some trial and error if it doesn't work the first try, since the x1000 manual is next to useless when it comes to pixel formats, what gets sent on the bus and why.
By the way, lcd_initialize_begin/end get called to power the panel on and off. Not sure exactly where and when they are called but their purpose seems clear enough.
I think you have pretty much everything you need to do a port. The pin labels seem clear enough that you can just guess where they go just based on the chip datasheets. Well, except for spdif_en. I can't make heads or tails of that. There's nothing SPDIF-related on this player, is there?
Oh, and did you find out the i2c bus layout yet? it's in board_base_init, those two calls near the end. What i2c chips are you expecting anyway? AXP192 is there, so aside from that is it just the DAC?
Yeah, the ring keys are the wheel, sorry I probably should have mentioned that!
It looks to me like the PCM5102A is only configured through discrete pins, and doesn't actually have any sort of serial configuration interface. It's "dumb" - just feed it I2S data and it does DAC things.
I wasn't sure if maybe lcd_initialize_begin/end were called one after another for some reason - lcd_initialize_end seems like a weird way to say "power off".
I do still need to try to get some info out of the I2C section - I haven't gotten that far yet. I think the AXP192 might be the only I2C device, unless the bluetooth controller is hooked up via I2C.
I was just starting to make a list of stuff to see what we have and how much more info is needed - I haven't really been keeping track while I've been digging for info. Although I suppose a lot of stuff could be glossed over in favor of just getting something to work first.
spdif
Yeah, I have no idea what that's about. Bluetooth maybe? I don't think there's an S/PDIF out on this...
-
Maybe you could comment on whether these are X1000 standard / dedicated hw connections? I don't think I need to find pin number information for:
Flash
LCD controller data
I2C (do need to know bus/address)
SD Card
-
Yep, all the interface pins are set in stone. There's no need to track any of those down, they're in the X1000 datasheet. You're right about the DAC, I had assumed it's I2C but after looking at the datasheet it is in fact "dumb pins." That kinda works out in your favor since there's less to twiddle with to get audio going.
I was looking over all the stuff you posted and everything important should be here. There might be some audio related pins missing, but it's hard to tell at a glance. LCD and buttons are the 2 biggest target-specific drivers, ironically. GPIOs are the biggest platform dependent thing when it comes to other drivers. Your flash is the same chip used in the M3K so it's supported already. The AXP192 is probably on I2C bus#2 and its address is fixed; so if that's the only I2C device, it's not really necessary to seek out that info.
LCD
---
24 bpp
8 bit bus
320x240
80 fps
power on
---
- PWR -> 1
- wait 20 ms
- RST -> 1
- wait 12 ms
power off
---
- PWR -> 0
- RST -> 0
backlight
---------
pwm0
30,000 ns period
gpios
-----
0x10 play key
0x11 volume up key
0x13 volume down key
0x25 out sel
0x26 analog power
0x27 backlight key
0x28 hp mute
0x29 lo power
0x2a msc pwr
0x2b msc cd
0x2c dac mute
0x2d lcd rst
0x2e lcd power
0x2f out mute
0x35 spdif en
0x37 ring1
0x38 ring2
0x3a rtc32k
0x3c menu key
0x53 bt reg on
0x58 prev key
0x59 BL PWR
0x64 next key
0x65 back key
It would be useful to find the AXP192 interrupt GPIO, but the Rockbox AXP driver can't use it yet. Headphone detection is usually wired to the AXP and I was planning to attach that to an interrupt instead of polling every 1/2 second like it currently does, but that's hardly super-important.
For the time being if you want to try running any code on your actual player you'll have to copy the M3K's SPL and monkey patch it until it works... the support is there for 32M memory size but it's totally untested and may be broken. There's another patch you need to do in order to USB boot Rockbox. Basically you just have to add these lines from the Q1 patch (https://gerrit.rockbox.org/r/c/rockbox/+/3460/1/firmware/target/mips/ingenic_x1000/system-x1000.c) in order to initialize the system clocks when you directly boot rockbox.bin over USB.
The current early boot situation is far from ideal, and pretty hard to understand in its current form (even for me, and I wrote it, lol). I'm in the middle of rewriting all the X1000 NAND code, and then I have to refactor the SPL stuff to allow for code re-use between the M3K and Q1. But anyhow, if you have trouble getting rockbox to boot, whenever you decide to do it, then let me know.
-
Excellent, thanks for holding my hand through this!
Just a couple quick "this should be obvious, but..." questions -
When you say the manual, you're referring to this X1000 Programming Manual? (https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&ved=2ahUKEwij_r6Yiq_xAhWOVs0KHSc4DMQQFjABegQIAxAE&url=https%3A%2F%2Fraw.githubusercontent.com%2Fxyfabc%2Fmy_books%2Fmaster%2FX1000_PM_20160113.pdf&usg=AOvVaw09hG-pz3slgxvpHTrL0AVW)
And it looks like the GPIOs map like this:
PA0-PA31 --> 0x00-0x1F
PB0-PB31 --> 0x20-0x3F
PC0-PC31 --> 0x40-0x5F
PD0-PD31 --> 0x60-0x6F
^^ Is that right?
-
One more data dump for posterity - I think I have some AXP stuff here. I wanted to find this so I could make sure the first attempts wouldn't set them wrong and kill whatever they're hooked up to...
regulator_init_data_ARRAY_80510ca8[1].*supply_ XREF[1,8]: 80510c38(*), 80510c44(*),
regulator_init_data_ARRAY_80510ca8[2].*supply_ 80510c50(*), 80510c5c(*),
regulator_init_data_ARRAY_80510ca8[3].*supply_ 80510c68(*), 80510c74(*),
regulator_init_data_ARRAY_80510ca8[4].*supply_ 80510c80(*), 80510c8c(*),
regulator_init_data_ARRAY_80510ca8[5].*supply_ 80510c98(*)
regulator_init_data_ARRAY_80510ca8[6].*supply_
regulator_init_data_ARRAY_80510ca8[7].*supply_
regulator_init_data_ARRAY_80510ca8[8].*supply_
regulator_init_data_ARRAY_80510ca8
80510ca8 00 00 00 regulato
00 5c 75
4b 80 d0
80510ca8 00 00 00 00 5c regulato [0] XREF[1]: 80510c38(*)
75 4b 80 d0 12
13 00 d0 12 13
80510ca8 00 00 00 00 char * 00000000 *supply_regu XREF[1]: 80510c38(*)
80510cac 5c 75 4b 80 d0 regulati constraints
12 13 00 d0 12
13 00 00 00 00
80510cac 5c 75 4b 80 char * s_axp_ldo1_804b755c *name = "axp_ldo1"
80510cb0 d0 12 13 00 int 1312D0h min_uV v output range - v
80510cb4 d0 12 13 00 int 1312D0h max_uV v output range - v
80510cb8 00 00 00 00 int 0h uV_offset
80510cbc 00 00 00 00 int 0h min_uA i output range - i
80510cc0 00 00 00 00 int 0h max_uA i output range - i
80510cc4 00 00 00 00 uint 0h valid_modes_
80510cc8 00 00 00 00 uint 0h valid_ops_mask
80510ccc 00 00 00 00 int 0h input_uV input v, if supply
80510cd0 00 00 00 00 00 regulato state_disk
00 00 00 00 00
00 00 00 00 00
80510cd0 00 00 00 00 int 0h uV suspend voltage
80510cd4 00 00 00 00 uint 0h mode suspend reg operat
80510cd8 00 00 00 00 int 0h enabled reg enabled in sus
80510cdc 00 00 00 00 int 0h disabled reg disabled in su
80510ce0 00 00 00 00 00 regulato state_mem
00 00 00 00 00
00 00 00 00 00
80510ce0 00 00 00 00 int 0h uV suspend voltage
80510ce4 00 00 00 00 uint 0h mode suspend reg operat
80510ce8 00 00 00 00 int 0h enabled reg enabled in sus
80510cec 00 00 00 00 int 0h disabled reg disabled in su
80510cf0 00 00 00 00 00 regulato state_standby
00 00 00 00 00
00 00 00 00 00
80510cf0 00 00 00 00 int 0h uV suspend voltage
80510cf4 00 00 00 00 uint 0h mode suspend reg operat
80510cf8 00 00 00 00 int 0h enabled reg enabled in sus
80510cfc 00 00 00 00 int 0h disabled reg disabled in su
80510d00 00 00 00 00 int 0h initial_state
80510d04 00 00 00 00 uint 0h initial_mode
80510d08 00 00 00 00 uint 0h ramp_delay
80510d0c 00 uint:1 0h always_on:1
80510d0c 00 uint:1 0h boot_on:1
80510d0c 00 00 00 00 uint:1 0h apply_uV apply uV constrain
80510d10 01 00 00 00 int 1h num_consumer
80510d14 0c 11 51 80 regulato DAT_8051110c *consumer_su
80510d18 00 00 00 00 int * 00000000 *regulator_i
80510d1c 00 00 00 00 void * 00000000 *driver_data
80510d20 00 00 00 00 68 regulato [1] XREF[1]: 80510c44(*)
75 4b 80 40 77
1b 00 a0 5a 32
80510d20 00 00 00 00 char * 00000000 *supply_regu XREF[1]: 80510c44(*)
80510d24 68 75 4b 80 40 regulati constraints
77 1b 00 a0 5a
32 00 00 00 00
80510d24 68 75 4b 80 char * s_axp_ldo2_804b7568 *name = "axp_ldo2"
80510d28 40 77 1b 00 int 1B7740h min_uV v output range - v
80510d2c a0 5a 32 00 int 325AA0h max_uV v output range - v
80510d30 00 00 00 00 int 0h uV_offset
80510d34 00 00 00 00 int 0h min_uA i output range - i
80510d38 00 00 00 00 int 0h max_uA i output range - i
80510d3c 00 00 00 00 uint 0h valid_modes_
80510d40 09 00 00 00 uint 9h valid_ops_mask
80510d44 00 00 00 00 int 0h input_uV input v, if supply
80510d48 00 00 00 00 00 regulato state_disk
00 00 00 00 00
00 00 00 00 00
80510d48 00 00 00 00 int 0h uV suspend voltage
80510d4c 00 00 00 00 uint 0h mode suspend reg operat
80510d50 00 00 00 00 int 0h enabled reg enabled in sus
80510d54 00 00 00 00 int 0h disabled reg disabled in su
80510d58 00 00 00 00 00 regulato state_mem
00 00 00 00 00
00 00 00 00 00
80510d58 00 00 00 00 int 0h uV suspend voltage
80510d5c 00 00 00 00 uint 0h mode suspend reg operat
80510d60 00 00 00 00 int 0h enabled reg enabled in sus
80510d64 00 00 00 00 int 0h disabled reg disabled in su
80510d68 a0 5a 32 00 00 regulato state_standby
00 00 00 01 00
00 00 00 00 00
80510d68 a0 5a 32 00 int 325AA0h uV suspend voltage
80510d6c 00 00 00 00 uint 0h mode suspend reg operat
80510d70 01 00 00 00 int 1h enabled reg enabled in sus
80510d74 00 00 00 00 int 0h disabled reg disabled in su
80510d78 02 00 00 00 int 2h initial_state
80510d7c 00 00 00 00 uint 0h initial_mode
80510d80 00 00 00 00 uint 0h ramp_delay
80510d84 00 uint:1 0h always_on:1
80510d84 00 uint:1 0h boot_on:1
80510d84 00 00 00 00 uint:1 0h apply_uV apply uV constrain
80510d88 01 00 00 00 int 1h num_consumer
80510d8c 14 11 51 80 regulato DAT_80511114 *consumer_su
80510d90 00 00 00 00 int * 00000000 *regulator_i
80510d94 00 00 00 00 void * 00000000 *driver_data
80510d98 00 00 00 00 74 regulato [2] XREF[1]: 80510c50(*)
75 4b 80 60 ae
0a 00 e0 67 35
80510d98 00 00 00 00 char * 00000000 *supply_regu XREF[1]: 80510c50(*)
80510d9c 74 75 4b 80 60 regulati constraints
ae 0a 00 e0 67
35 00 00 00 00
80510d9c 74 75 4b 80 char * s_axp_ldo3_804b7574 *name = "axp_ldo3"
80510da0 60 ae 0a 00 int AAE60h min_uV v output range - v
80510da4 e0 67 35 00 int 3567E0h max_uV v output range - v
80510da8 00 00 00 00 int 0h uV_offset
80510dac 00 00 00 00 int 0h min_uA i output range - i
80510db0 00 00 00 00 int 0h max_uA i output range - i
80510db4 00 00 00 00 uint 0h valid_modes_
80510db8 09 00 00 00 uint 9h valid_ops_mask
80510dbc 00 00 00 00 int 0h input_uV input v, if supply
80510dc0 00 00 00 00 00 regulato state_disk
00 00 00 00 00
00 00 00 00 00
80510dc0 00 00 00 00 int 0h uV suspend voltage
80510dc4 00 00 00 00 uint 0h mode suspend reg operat
80510dc8 00 00 00 00 int 0h enabled reg enabled in sus
80510dcc 00 00 00 00 int 0h disabled reg disabled in su
80510dd0 00 00 00 00 00 regulato state_mem
00 00 00 00 00
00 00 00 00 00
80510dd0 00 00 00 00 int 0h uV suspend voltage
80510dd4 00 00 00 00 uint 0h mode suspend reg operat
80510dd8 00 00 00 00 int 0h enabled reg enabled in sus
80510ddc 00 00 00 00 int 0h disabled reg disabled in su
80510de0 00 00 00 00 00 regulato state_standby
00 00 00 00 00
00 00 00 00 00
80510de0 00 00 00 00 int 0h uV suspend voltage
80510de4 00 00 00 00 uint 0h mode suspend reg operat
80510de8 00 00 00 00 int 0h enabled reg enabled in sus
80510dec 00 00 00 00 int 0h disabled reg disabled in su
80510df0 00 00 00 00 int 0h initial_state
80510df4 00 00 00 00 uint 0h initial_mode
80510df8 00 00 00 00 uint 0h ramp_delay
80510dfc 00 uint:1 0h always_on:1
80510dfc 00 uint:1 0h boot_on:1
80510dfc 00 00 00 00 uint:1 0h apply_uV apply uV constrain
80510e00 01 00 00 00 int 1h num_consumer
80510e04 1c 11 51 80 regulato DAT_8051111c *consumer_su
80510e08 00 00 00 00 int * 00000000 *regulator_i
80510e0c 00 00 00 00 void * 00000000 *driver_data
80510e10 00 00 00 00 80 regulato [3] XREF[1]: 80510c5c(*)
75 4b 80 40 77
1b 00 a0 5a 32
80510e10 00 00 00 00 char * 00000000 *supply_regu XREF[1]: 80510c5c(*)
80510e14 80 75 4b 80 40 regulati constraints
77 1b 00 a0 5a
32 00 00 00 00
80510e14 80 75 4b 80 char * s_axp_ldo4_804b7580 *name = "axp_ldo4"
80510e18 40 77 1b 00 int 1B7740h min_uV v output range - v
80510e1c a0 5a 32 00 int 325AA0h max_uV v output range - v
80510e20 00 00 00 00 int 0h uV_offset
80510e24 00 00 00 00 int 0h min_uA i output range - i
80510e28 00 00 00 00 int 0h max_uA i output range - i
80510e2c 00 00 00 00 uint 0h valid_modes_
80510e30 09 00 00 00 uint 9h valid_ops_mask
80510e34 00 00 00 00 int 0h input_uV input v, if supply
80510e38 00 00 00 00 00 regulato state_disk
00 00 00 00 00
00 00 00 00 00
80510e38 00 00 00 00 int 0h uV suspend voltage
80510e3c 00 00 00 00 uint 0h mode suspend reg operat
80510e40 00 00 00 00 int 0h enabled reg enabled in sus
80510e44 00 00 00 00 int 0h disabled reg disabled in su
80510e48 00 00 00 00 00 regulato state_mem
00 00 00 00 00
00 00 00 00 00
80510e48 00 00 00 00 int 0h uV suspend voltage
80510e4c 00 00 00 00 uint 0h mode suspend reg operat
80510e50 00 00 00 00 int 0h enabled reg enabled in sus
80510e54 00 00 00 00 int 0h disabled reg disabled in su
80510e58 00 00 00 00 00 regulato state_standby
00 00 00 00 00
00 00 00 00 00
80510e58 00 00 00 00 int 0h uV suspend voltage
80510e5c 00 00 00 00 uint 0h mode suspend reg operat
80510e60 00 00 00 00 int 0h enabled reg enabled in sus
80510e64 00 00 00 00 int 0h disabled reg disabled in su
80510e68 00 00 00 00 int 0h initial_state
80510e6c 00 00 00 00 uint 0h initial_mode
80510e70 00 00 00 00 uint 0h ramp_delay
80510e74 00 uint:1 0h always_on:1
80510e74 00 uint:1 0h boot_on:1
80510e74 00 00 00 00 uint:1 0h apply_uV apply uV constrain
80510e78 01 00 00 00 int 1h num_consumer
80510e7c 24 11 51 80 regulato DAT_80511124 *consumer_su
80510e80 00 00 00 00 int * 00000000 *regulator_i
80510e84 00 00 00 00 void * 00000000 *driver_data
80510e88 00 00 00 00 8c regulato [4] XREF[1]: 80510c68(*)
75 4b 80 60 ae
0a 00 e0 67 35
80510e88 00 00 00 00 char * 00000000 *supply_regu XREF[1]: 80510c68(*)
80510e8c 8c 75 4b 80 60 regulati constraints
ae 0a 00 e0 67
35 00 00 00 00
80510e8c 8c 75 4b 80 char * s_axp_buck1_804b758c *name = "axp_buck1"
80510e90 60 ae 0a 00 int AAE60h min_uV v output range - v
80510e94 e0 67 35 00 int 3567E0h max_uV v output range - v
80510e98 00 00 00 00 int 0h uV_offset
80510e9c 00 00 00 00 int 0h min_uA i output range - i
80510ea0 00 00 00 00 int 0h max_uA i output range - i
80510ea4 00 00 00 00 uint 0h valid_modes_
80510ea8 09 00 00 00 uint 9h valid_ops_mask
80510eac 00 00 00 00 int 0h input_uV input v, if supply
80510eb0 00 00 00 00 00 regulato state_disk
00 00 00 00 00
00 00 00 00 00
80510eb0 00 00 00 00 int 0h uV suspend voltage
80510eb4 00 00 00 00 uint 0h mode suspend reg operat
80510eb8 00 00 00 00 int 0h enabled reg enabled in sus
80510ebc 00 00 00 00 int 0h disabled reg disabled in su
80510ec0 00 00 00 00 00 regulato state_mem
00 00 00 00 00
00 00 00 00 00
80510ec0 00 00 00 00 int 0h uV suspend voltage
80510ec4 00 00 00 00 uint 0h mode suspend reg operat
80510ec8 00 00 00 00 int 0h enabled reg enabled in sus
80510ecc 00 00 00 00 int 0h disabled reg disabled in su
80510ed0 00 00 00 00 00 regulato state_standby
00 00 00 00 00
00 00 00 00 00
80510ed0 00 00 00 00 int 0h uV suspend voltage
80510ed4 00 00 00 00 uint 0h mode suspend reg operat
80510ed8 00 00 00 00 int 0h enabled reg enabled in sus
80510edc 00 00 00 00 int 0h disabled reg disabled in su
80510ee0 00 00 00 00 int 0h initial_state
80510ee4 00 00 00 00 uint 0h initial_mode
80510ee8 00 00 00 00 uint 0h ramp_delay
80510eec 00 uint:1 0h always_on:1
80510eec 00 uint:1 0h boot_on:1
80510eec 00 00 00 00 uint:1 0h apply_uV apply uV constrain
80510ef0 01 00 00 00 int 1h num_consumer
80510ef4 2c 11 51 80 regulato DAT_8051112c *consumer_su
80510ef8 00 00 00 00 int * 00000000 *regulator_i
80510efc 00 00 00 00 void * 00000000 *driver_data
80510f00 00 00 00 00 98 regulato [5] XREF[1]: 80510c74(*)
75 4b 80 60 ae
0a 00 b8 b6 22
80510f00 00 00 00 00 char * 00000000 *supply_regu XREF[1]: 80510c74(*)
80510f04 98 75 4b 80 60 regulati constraints
ae 0a 00 b8 b6
22 00 00 00 00
80510f04 98 75 4b 80 char * s_axp_buck2_804b7598 *name = "axp_buck2"
80510f08 60 ae 0a 00 int AAE60h min_uV v output range - v
80510f0c b8 b6 22 00 int 22B6B8h max_uV v output range - v
80510f10 00 00 00 00 int 0h uV_offset
80510f14 00 00 00 00 int 0h min_uA i output range - i
80510f18 00 00 00 00 int 0h max_uA i output range - i
80510f1c 00 00 00 00 uint 0h valid_modes_
80510f20 09 00 00 00 uint 9h valid_ops_mask
80510f24 00 00 00 00 int 0h input_uV input v, if supply
80510f28 00 00 00 00 00 regulato state_disk
00 00 00 00 00
00 00 00 00 00
80510f28 00 00 00 00 int 0h uV suspend voltage
80510f2c 00 00 00 00 uint 0h mode suspend reg operat
80510f30 00 00 00 00 int 0h enabled reg enabled in sus
80510f34 00 00 00 00 int 0h disabled reg disabled in su
80510f38 00 00 00 00 00 regulato state_mem
00 00 00 00 00
00 00 00 00 00
80510f38 00 00 00 00 int 0h uV suspend voltage
80510f3c 00 00 00 00 uint 0h mode suspend reg operat
80510f40 00 00 00 00 int 0h enabled reg enabled in sus
80510f44 00 00 00 00 int 0h disabled reg disabled in su
80510f48 00 00 00 00 00 regulato state_standby
00 00 00 00 00
00 00 00 00 00
80510f48 00 00 00 00 int 0h uV suspend voltage
80510f4c 00 00 00 00 uint 0h mode suspend reg operat
80510f50 00 00 00 00 int 0h enabled reg enabled in sus
80510f54 00 00 00 00 int 0h disabled reg disabled in su
80510f58 00 00 00 00 int 0h initial_state
80510f5c 00 00 00 00 uint 0h initial_mode
80510f60 00 00 00 00 uint 0h ramp_delay
80510f64 00 uint:1 0h always_on:1
80510f64 00 uint:1 0h boot_on:1
80510f64 00 00 00 00 uint:1 0h apply_uV apply uV constrain
80510f68 01 00 00 00 int 1h num_consumer
80510f6c 34 11 51 80 regulato DAT_80511134 *consumer_su
80510f70 00 00 00 00 int * 00000000 *regulator_i
80510f74 00 00 00 00 void * 00000000 *driver_data
80510f78 00 00 00 00 a4 regulato [6] XREF[1]: 80510c80(*)
75 4b 80 60 ae
0a 00 e0 67 35
80510f78 00 00 00 00 char * 00000000 *supply_regu XREF[1]: 80510c80(*)
80510f7c a4 75 4b 80 60 regulati constraints
ae 0a 00 e0 67
35 00 00 00 00
80510f7c a4 75 4b 80 char * s_axp_buck3_804b75a4 *name = "axp_buck3"
80510f80 60 ae 0a 00 int AAE60h min_uV v output range - v
80510f84 e0 67 35 00 int 3567E0h max_uV v output range - v
80510f88 00 00 00 00 int 0h uV_offset
80510f8c 00 00 00 00 int 0h min_uA i output range - i
80510f90 00 00 00 00 int 0h max_uA i output range - i
80510f94 00 00 00 00 uint 0h valid_modes_
80510f98 09 00 00 00 uint 9h valid_ops_mask
80510f9c 00 00 00 00 int 0h input_uV input v, if supply
80510fa0 00 00 00 00 00 regulato state_disk
00 00 00 00 00
00 00 00 00 00
80510fa0 00 00 00 00 int 0h uV suspend voltage
80510fa4 00 00 00 00 uint 0h mode suspend reg operat
80510fa8 00 00 00 00 int 0h enabled reg enabled in sus
80510fac 00 00 00 00 int 0h disabled reg disabled in su
80510fb0 00 00 00 00 00 regulato state_mem
00 00 00 00 00
00 00 00 00 00
80510fb0 00 00 00 00 int 0h uV suspend voltage
80510fb4 00 00 00 00 uint 0h mode suspend reg operat
80510fb8 00 00 00 00 int 0h enabled reg enabled in sus
80510fbc 00 00 00 00 int 0h disabled reg disabled in su
80510fc0 00 00 00 00 00 regulato state_standby
00 00 00 00 00
00 00 00 00 00
80510fc0 00 00 00 00 int 0h uV suspend voltage
80510fc4 00 00 00 00 uint 0h mode suspend reg operat
80510fc8 00 00 00 00 int 0h enabled reg enabled in sus
80510fcc 00 00 00 00 int 0h disabled reg disabled in su
80510fd0 00 00 00 00 int 0h initial_state
80510fd4 00 00 00 00 uint 0h initial_mode
80510fd8 00 00 00 00 uint 0h ramp_delay
80510fdc 00 uint:1 0h always_on:1
80510fdc 00 uint:1 0h boot_on:1
80510fdc 00 00 00 00 uint:1 0h apply_uV apply uV constrain
80510fe0 01 00 00 00 int 1h num_consumer
80510fe4 3c 11 51 80 regulato DAT_8051113c *consumer_su
80510fe8 00 00 00 00 int * 00000000 *regulator_i
80510fec 00 00 00 00 void * 00000000 *driver_data
80510ff0 00 00 00 00 b0 regulato [7] XREF[1]: 80510c8c(*)
75 4b 80 60 ae
0a 00 e0 67 35
80510ff0 00 00 00 00 char * 00000000 *supply_regu XREF[1]: 80510c8c(*)
80510ff4 b0 75 4b 80 60 regulati constraints
ae 0a 00 e0 67
35 00 00 00 00
80510ff4 b0 75 4b 80 char * s_axp_buck4_804b75b0 *name = "axp_buck4"
80510ff8 60 ae 0a 00 int AAE60h min_uV v output range - v
80510ffc e0 67 35 00 int 3567E0h max_uV v output range - v
80511000 00 00 00 00 int 0h uV_offset
80511004 00 00 00 00 int 0h min_uA i output range - i
80511008 00 00 00 00 int 0h max_uA i output range - i
8051100c 00 00 00 00 uint 0h valid_modes_
80511010 09 00 00 00 uint 9h valid_ops_mask
80511014 00 00 00 00 int 0h input_uV input v, if supply
80511018 00 00 00 00 00 regulato state_disk
00 00 00 00 00
00 00 00 00 00
80511018 00 00 00 00 int 0h uV suspend voltage
8051101c 00 00 00 00 uint 0h mode suspend reg operat
80511020 00 00 00 00 int 0h enabled reg enabled in sus
80511024 00 00 00 00 int 0h disabled reg disabled in su
80511028 00 00 00 00 00 regulato state_mem
00 00 00 00 00
00 00 00 00 00
80511028 00 00 00 00 int 0h uV suspend voltage
8051102c 00 00 00 00 uint 0h mode suspend reg operat
80511030 00 00 00 00 int 0h enabled reg enabled in sus
80511034 00 00 00 00 int 0h disabled reg disabled in su
80511038 00 00 00 00 00 regulato state_standby
00 00 00 00 00
00 00 00 00 00
80511038 00 00 00 00 int 0h uV suspend voltage
8051103c 00 00 00 00 uint 0h mode suspend reg operat
80511040 00 00 00 00 int 0h enabled reg enabled in sus
80511044 00 00 00 00 int 0h disabled reg disabled in su
80511048 00 00 00 00 int 0h initial_state
8051104c 00 00 00 00 uint 0h initial_mode
80511050 00 00 00 00 uint 0h ramp_delay
80511054 00 uint:1 0h always_on:1
80511054 00 uint:1 0h boot_on:1
80511054 00 00 00 00 uint:1 0h apply_uV apply uV constrain
80511058 01 00 00 00 int 1h num_consumer
8051105c 44 11 51 80 regulato DAT_80511144 *consumer_su
80511060 00 00 00 00 int * 00000000 *regulator_i
80511064 00 00 00 00 void * 00000000 *driver_data
80511068 00 00 00 00 bc regulato [8] XREF[1]: 80510c98(*)
75 4b 80 40 77
1b 00 a0 5a 32
80511068 00 00 00 00 char * 00000000 *supply_regu XREF[1]: 80510c98(*)
8051106c bc 75 4b 80 40 regulati constraints
77 1b 00 a0 5a
32 00 00 00 00
8051106c bc 75 4b 80 char * s_axp_ldoio0_804b75bc *name = "axp_ldoio0"
80511070 40 77 1b 00 int 1B7740h min_uV v output range - v
80511074 a0 5a 32 00 int 325AA0h max_uV v output range - v
80511078 00 00 00 00 int 0h uV_offset
8051107c 00 00 00 00 int 0h min_uA i output range - i
80511080 00 00 00 00 int 0h max_uA i output range - i
80511084 00 00 00 00 uint 0h valid_modes_
80511088 09 00 00 00 uint 9h valid_ops_mask
8051108c 00 00 00 00 int 0h input_uV input v, if supply
80511090 00 00 00 00 00 regulato state_disk
00 00 00 00 00
00 00 00 00 00
80511090 00 00 00 00 int 0h uV suspend voltage
80511094 00 00 00 00 uint 0h mode suspend reg operat
80511098 00 00 00 00 int 0h enabled reg enabled in sus
8051109c 00 00 00 00 int 0h disabled reg disabled in su
805110a0 00 00 00 00 00 regulato state_mem
00 00 00 00 00
00 00 00 00 00
805110a0 00 00 00 00 int 0h uV suspend voltage
805110a4 00 00 00 00 uint 0h mode suspend reg operat
805110a8 00 00 00 00 int 0h enabled reg enabled in sus
805110ac 00 00 00 00 int 0h disabled reg disabled in su
805110b0 00 00 00 00 00 regulato state_standby
00 00 00 00 00
00 00 00 00 00
805110b0 00 00 00 00 int 0h uV suspend voltage
805110b4 00 00 00 00 uint 0h mode suspend reg operat
805110b8 00 00 00 00 int 0h enabled reg enabled in sus
805110bc 00 00 00 00 int 0h disabled reg disabled in su
805110c0 00 00 00 00 int 0h initial_state
805110c4 00 00 00 00 uint 0h initial_mode
805110c8 00 00 00 00 uint 0h ramp_delay
805110cc 00 uint:1 0h always_on:1
805110cc 00 uint:1 0h boot_on:1
805110cc 00 00 00 00 uint:1 0h apply_uV apply uV constrain
805110d0 01 00 00 00 int 1h num_consumer
805110d4 4c 11 51 80 regulato DAT_8051114c *consumer_su
805110d8 00 00 00 00 int * 00000000 *regulator_i
805110dc 00 00 00 00 void * 00000000 *driver_data
axp_supply_init_data_805110e0 XREF[1]: 80510ca4(*)
805110e0 54 11 51 axp_supp
80 20 03
00 00 20
805110e0 54 11 51 80 power_su power_supply_info_8051 *battery_info = XREF[1]: 80510ca4(*)
805110e4 20 03 00 00 uint 320h chgcur
805110e8 20 03 00 00 uint 320h chgearcur
805110ec 20 03 00 00 uint 320h chgsuscur
805110f0 20 03 00 00 uint 320h chgclscur
805110f4 68 10 00 00 uint 1068h chgvol
805110f8 0a 00 00 00 uint Ah chgend
805110fc 00 00 00 00 int 0h limit_on
80511100 32 00 00 00 uint 32h chgpretime
80511104 e0 01 00 00 uint 1E0h chgcsttime
80511108 19 00 00 00 uint 19h adc_freq
DAT_8051110c XREF[1]: 80510d14(*)
8051110c 00 ?? 00h
8051110d 00 ?? 00h
8051110e 00 ?? 00h
8051110f 00 ?? 00h
80511110 60 75 4b 80 addr s_ldo1_804b755c+4 = "ldo1"
DAT_80511114 XREF[1]: 80510d8c(*)
80511114 00 ?? 00h
80511115 00 ?? 00h
80511116 00 ?? 00h
80511117 00 ?? 00h
80511118 6c 75 4b 80 addr s_ldo2_804b7568+4 = "ldo2"
DAT_8051111c XREF[1]: 80510e04(*)
8051111c 00 ?? 00h
8051111d 00 ?? 00h
8051111e 00 ?? 00h
8051111f 00 ?? 00h
80511120 78 75 4b 80 addr s_ldo3_804b7574+4 = "ldo3"
DAT_80511124 XREF[1]: 80510e7c(*)
80511124 00 ?? 00h
80511125 00 ?? 00h
80511126 00 ?? 00h
80511127 00 ?? 00h
80511128 84 75 4b 80 addr s_ldo4_804b7580+4 = "ldo4"
DAT_8051112c XREF[1]: 80510ef4(*)
8051112c 00 ?? 00h
8051112d 00 ?? 00h
8051112e 00 ?? 00h
8051112f 00 ?? 00h
80511130 c8 75 4b 80 addr s_dcdc1_804b75c8 = "dcdc1"
DAT_80511134 XREF[1]: 80510f6c(*)
80511134 00 ?? 00h
80511135 00 ?? 00h
80511136 00 ?? 00h
80511137 00 ?? 00h
80511138 d0 75 4b 80 addr s_vdd_cpu_804b75d0 = "vdd_cpu"
DAT_8051113c XREF[1]: 80510fe4(*)
8051113c 00 ?? 00h
8051113d 00 ?? 00h
8051113e 00 ?? 00h
8051113f 00 ?? 00h
80511140 d8 75 4b 80 addr s_dcdc3_804b75d8 = "dcdc3"
DAT_80511144 XREF[1]: 8051105c(*)
80511144 00 ?? 00h
80511145 00 ?? 00h
80511146 00 ?? 00h
80511147 00 ?? 00h
80511148 e0 75 4b 80 addr s_vdd_core_804b75e0 = "vdd_core"
DAT_8051114c XREF[1]: 805110d4(*)
8051114c 00 ?? 00h
8051114d 00 ?? 00h
8051114e 00 ?? 00h
8051114f 00 ?? 00h
80511150 c0 75 4b 80 addr s_ldoio0_804b75bc+4 = "ldoio0"
power_supply_info_80511154 XREF[1]: 805110e0(*)
80511154 ec 75 4b power_su
80 02 00
00 00 68
80511154 ec 75 4b 80 char * s_EASTROAD_804b75ec name = "EASTROAD" XREF[1]: 805110e0(*)
80511158 02 00 00 00 int 2h technology
8051115c 68 10 00 00 int 1068h voltage_max_
80511160 48 0d 00 00 int D48h voltage_min_
80511164 00 00 00 00 int 0h charge_full_
80511168 00 00 00 00 int 0h charge_empty
8051116c e4 0c 00 00 int CE4h energy_full_
80511170 00 00 00 00 int 0h energy_empty
80511174 00 00 00 00 int 0h use_for_apm
80511178 00 ?? 00h
80511179 00 ?? 00h
8051117a 00 ?? 00h
Notes from above:
dcdc1: 0.70 - 3.50
dcdc2: 0.70 - 2.275 *
dcdc3: 0.70 - 3.50
dcdc4: 0.70 - 3.50 **
ldo1: 1.25 - 1.25
ldo2: 1.80 - 3.30 ***
ldo3: 0.70 - 3.50
ldo4: 1.80 - 3.30
ldo io 0: 1.80 - 3.30
* dcdc2 notes: labelled "vdd_cpu"
**dcdc4 notes: labelled "vdd_core"
*** ldo2 notes: Standby state enabled, 3.30V, initial state "2"
-
Yeah that's the manual I mean, and you have the GPIO numbering right. I have a "later" version of the manual which I got from Ingenic's FTP last year, marked October 2018. I don't know if there are any meaningful differences.
Re. power supply voltages -- after a reset all voltages will return to a default setting, and they can't be changed until fairly late in the boot. The ranges you got are simply ranges supported by the chip, and somewhere else, there may be an actual voltage setting. That might be in a data table, or could be buried inside a function call; who knows. But if none of the defaults are changed, then there may be nothing to see, unless they decided to redundantly set the defaults somewhere. The OF should be able to report the voltages through sysfs; I don't remember the full paths, but they are named axp_supplyer.N. I think that's how I learned about the Q1's voltage levels, because digging in the kernel wasn't revealing much. The M3K just used default voltages.
X1000 and AXP192 datasheets: https://drive.google.com/drive/folders/19Z_nSW88B7ibtFly0IaWw-yRYT97t9e7 (https://drive.google.com/drive/folders/19Z_nSW88B7ibtFly0IaWw-yRYT97t9e7)
-
So I've got enough code into this gerrit patch (https://gerrit.rockbox.org/r/c/rockbox/+/3506) that it at least successfully compiles. It is definitely rough. I haven't successfully gotten any sort of life signs out of the player yet though - the backlight doesn't light, and the LCD doesn't show anything when I shine a flashlight into it.
I've been trying to use usbboot with:
[root@localhost ingenic_tools]# ./usbboot --cpu x1000 --stage1 ../../build-erosqnative-boot/spl.erosq --wait 5 --stage2 ../../build-erosqnative/rockbox.erosq
Is there anything super-obviously-wrong with what I'm doing - or my patchset?
-
The command line is wrong: use rockbox.bin. The scramble image has 8 bytes of header garbage at the beginning and obviously nothing will work if you get an illegal instruction right at the beginning of execution... assuming it gets that far. Does usbboot report everything is loaded? If you get an error on the 2nd stage it's a problem in the SPL (Use --verbose as the first argument)
I'll check your patch sometime tomorrow. I've been up till 2am trying to debug a weird issue with the shanling and my 256gb card with no luck. So, got to sleep now. Have fun with your problems!
-
use rockbox.bin.
Aha... That would definitely be a problem, even if it isn't the problem. I'm still not getting any signs of life, but it looks like usbboot is happy enough with it now:
[root@localhost ingenic_tools]# ./usbboot --verbose --cpu x1000 --stage1 ../../build-erosqnative-boot/spl.erosq --wait 5 --stage2 ../../build-erosqnative/rockbox.bin
Opening USB device a108:1000
Issue SET_DATA_ADDRESS 0xf4001000
Transfer 10296 bytes from host to device
Issue PROGRAM_START1 0xf4001800
Wait 5 seconds
Issue SET_DATA_ADDRESS 0x80004000
Transfer 746968 bytes from host to device
Issue FLUSH_CACHES
Issue PROGRAM_START2 0x80004000
[root@localhost ingenic_tools]#
I'll keep poking at it tomorrow, definitely don't feel like you have to rush to review my patchset by any means.
Edit: Unless you meant that the spl binary is also subject to this scramble header and needs to be .bin? I thought I had tried that and got an error message, but I'll try again in the morning.
-
Life!
I can get the screen to flash the backlight by calling spl_error() from just after init in spl-x1000.c:
void spl_main(void)
{
/* Basic hardware init */
init();
spl_error();
/* If doing a USB boot, host PC will upload 2nd stage itself,
* we should not load anything from flash or change clocks. */
if((boot_sel & 3) == 2)
return;
/* Just pass control to the target... */
spl_target_boot();
}
It's not much, but it's a starting point. It looks like in the usb boot case, this function returns, but what gets called after that? (aka... where can I put my flashy lights to follow?)
Edit: Looks like spl_error() doesn't flash the backlight if I call it from system_init() in system-x1000.c:
void system_init(void)
{
spl_error();
// HACK FIXME: this is only for USB booting Rockbox, REMOVE ME
spl_handle_pre_boot(0);
/* 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));
jz_clrf(OST_ENABLE, OST1, OST2);
jz_write(OST_1MSK, 1);
jz_write(OST_1FLG, 0);
jz_clr(TCU_ENABLE, 0x80ff);
jz_set(TCU_MASK, 0xff10ff);
jz_clr(TCU_FLAG, 0xff10ff);
jz_set(TCU_STOP, 0x180ff);
-
Edit: Unless you meant that the spl binary is also subject to this scramble header and needs to be .bin? I thought I had tried that and got an error message, but I'll try again in the morning.
You do need to load spl.erosq with usbboot and not spl.bin. The .erosq file has a different header appended to it, generated by tools/mkspl-x1000.c, not the scramble header. The SPL header gets read by the CPU's boot ROM when booting from flash or SD. In the case of USB boot, I'm not 100% certain whether the header is necessary but it would only be useful for the CPU's "secure boot" mode, which isn't being used -- if it was, presumably you would've had a problem getting code to run at all. In any event, the usbboot tool is written to expect a file with the SPL header, so loading spl.bin using the --stage1 argument won't work.
It's not much, but it's a starting point. It looks like in the usb boot case, this function returns, but what gets called after that? (aka... where can I put my flashy lights to follow?)
The entry point is ingenic_x1000/crt0.S, from there it calls main() in apps/main.c and the first thing called after that is system_init().
Edit: Looks like spl_error() doesn't flash the backlight if I call it from system_init() in system-x1000.c:
You will have to take a close look at the memory initialization. It could be wrong for your player, since I only have 64MB X1000E SoCs to test with and not the 32MB X1000. The first thing I would check is to see if you have any memory corruption going on - this happened to me very early on because I didn't set up the DRAM self refresh properly, as a result any memory which wasn't accessed within a short span of time would corrupt because the memory controller wasn't sending the necessary refresh commands. So to check for any corruption, try putting this in the SPL:
void spl_main(void)
{
/* Basic hardware init */
init();
memset((void*)0xa0000000, 0, 1024 * 1024);
return;
}
That's zeroing out the first 1 MiB of memory using an uncached address to bypass the CPU cache. Run it like this and dump out that first 1M of memory:
usbboot -v -c x1000 -1 <spl.erosq>
usbboot -v -c x1000 --addr 0xa0000000 --length 1048576 --upload memory0.bin
Wait 1 minute or so, then dump the memory contents again:
usbboot -v -c x1000 --addr 0xa0000000 --length 1048576 --upload memory1.bin
hexdump them both and verify that both files are full of zeros as they should be. Repeat this a few times and wait for progressively longer intervals between dumps (arbitrarily, I would try up to 5 minutes just to be on the safe side). Keep the player plugged in, turned on, and don't re-run the SPL, only do the "upload" command. Of course, you should get a file full of zeros every time, but if you see more and more nonzero bytes appearing then DRAM is probably not refreshing correctly.
It could also be another DRAM related problem, eg. incorrect memory mappings or bad initialization. Or maybe even a non-DRAM problem, but I find it unlikely that DRAM would be okay and you can't get past the first few instructions. I just think DRAM corruption is the first and easiest thing to check for, so I'd do that first.
This is the source code for Ingenic's SPL: https://github.com/JaminCheung/x-loader (https://github.com/JaminCheung/x-loader). There's also some in the YuanhuanLiang repo. If it is really a memory related issue, you might start by comparing stuff from there... I wasn't able to find the x-loader source code myself so I simply reverse-engineered the M3K's SPL to write the memory initialization for Rockbox's SPL. Also useful, https://www.jedec.org/ (https://www.jedec.org/) lets you freely download the DDR2 spec, among other things, but you need to create an account first (requires a working email to activate the account; redistribution of their documents isn't allowed).
Here's some useful links for MIPS architecture stuff -- for example, the privileged resource architecture manual tells you how the virtual address space is laid out. Most of this is not directly useful for getting Rockbox running (ie, you probably won't need to actually code anything based on this info), but it's good background material.
- https://www.mips.com/downloads/introduction-to-the-mips32-architecture-v6-01/ (https://www.mips.com/downloads/introduction-to-the-mips32-architecture-v6-01/)
- https://www.mips.com/downloads/the-mips32-instruction-set-v6-06/ (https://www.mips.com/downloads/the-mips32-instruction-set-v6-06/)
- https://www.mips.com/downloads/the-mips32-and-micromips32-privileged-resource-architecture-v6-02/ (https://www.mips.com/downloads/the-mips32-and-micromips32-privileged-resource-architecture-v6-02/)
Just keep in mind those manuals have some things in there for multiple revisions of the MIPS architecture. The X1000 is a quasi-MIPS32 Release 2 CPU: it implements the integer r2 instructions according to its manual and from my limited testing, but it doesn't implement any useful stuff from the r2 PRA, and it's even missing some minor stuff from the base Release 1 PRA.
Anyway good luck! Let me know what you find out. At least you've got some progress :)
-
Edit: Looks like spl_error() doesn't flash the backlight if I call it from system_init() in system-x1000.c:
You will have to take a close look at the memory initialization. It could be wrong for your player, since I only have 64MB X1000E SoCs to test with and not the 32MB X1000. The first thing I would check is to see if you have any memory corruption going on - this happened to me very early on because I didn't set up the DRAM self refresh properly, as a result any memory which wasn't accessed within a short span of time would corrupt because the memory controller wasn't sending the necessary refresh commands. So to check for any corruption, try putting this in the SPL:
void spl_main(void)
{
/* Basic hardware init */
init();
memset((void*)0xa0000000, 0, 1024 * 1024);
return;
}
That's zeroing out the first 1 MiB of memory using an uncached address to bypass the CPU cache. Run it like this and dump out that first 1M of memory:
usbboot -v -c x1000 -1 <spl.erosq>
usbboot -v -c x1000 --addr 0xa0000000 --length 1048576 --upload memory0.bin
Wait 1 minute or so, then dump the memory contents again:
usbboot -v -c x1000 --addr 0xa0000000 --length 1048576 --upload memory1.bin
hexdump them both and verify that both files are full of zeros as they should be. Repeat this a few times and wait for progressively longer intervals between dumps (arbitrarily, I would try up to 5 minutes just to be on the safe side). Keep the player plugged in, turned on, and don't re-run the SPL, only do the "upload" command. Of course, you should get a file full of zeros every time, but if you see more and more nonzero bytes appearing then DRAM is probably not refreshing correctly.
It could also be another DRAM related problem, eg. incorrect memory mappings or bad initialization. Or maybe even a non-DRAM problem, but I find it unlikely that DRAM would be okay and you can't get past the first few instructions. I just think DRAM corruption is the first and easiest thing to check for, so I'd do that first.
Well, I took several hex dumps over the course of about 15 minutes, and didn't see any non-zero values at all, so it looks like I'll have to start looking into the DRAM stuff.
This is the source code for Ingenic's SPL: https://github.com/JaminCheung/x-loader (https://github.com/JaminCheung/x-loader). There's also some in the YuanhuanLiang repo. If it is really a memory related issue, you might start by comparing stuff from there... I wasn't able to find the x-loader source code myself so I simply reverse-engineered the M3K's SPL to write the memory initialization for Rockbox's SPL. Also useful, https://www.jedec.org/ (https://www.jedec.org/) lets you freely download the DDR2 spec, among other things, but you need to create an account first (requires a working email to activate the account; redistribution of their documents isn't allowed).
Here's some useful links for MIPS architecture stuff -- for example, the privileged resource architecture manual tells you how the virtual address space is laid out. Most of this is not directly useful for getting Rockbox running (ie, you probably won't need to actually code anything based on this info), but it's good background material.
- https://www.mips.com/downloads/introduction-to-the-mips32-architecture-v6-01/ (https://www.mips.com/downloads/introduction-to-the-mips32-architecture-v6-01/)
- https://www.mips.com/downloads/the-mips32-instruction-set-v6-06/ (https://www.mips.com/downloads/the-mips32-instruction-set-v6-06/)
- https://www.mips.com/downloads/the-mips32-and-micromips32-privileged-resource-architecture-v6-02/ (https://www.mips.com/downloads/the-mips32-and-micromips32-privileged-resource-architecture-v6-02/)
Just keep in mind those manuals have some things in there for multiple revisions of the MIPS architecture. The X1000 is a quasi-MIPS32 Release 2 CPU: it implements the integer r2 instructions according to its manual and from my limited testing, but it doesn't implement any useful stuff from the r2 PRA, and it's even missing some minor stuff from the base Release 1 PRA.
Anyway good luck! Let me know what you find out. At least you've got some progress :)
Thanks for the resources! I'll get started.
-
Hmm, if your DRAM contents are stable the issue might be elsewhere. I suspected DRAM because it's something very early on and not tested on your HW configuration. The only other DRAM-related thing which comes to mind is the addressing bits REG_DDRC_REMAPn, so you might try to confirm that the address mapping works by loop over all memory in the SPL like this:--
void spl_main(void)
{
/* Basic hardware init */
init();
uint32_t* ptr = (uint32_t*)0xa0000000;
unsigned count = 32 * 1024 * 1024 / 4;
for(unsigned i = 0; i < count; ++i)
*ptr++ = i;
return;
}
then grab the memory contents in 1M chunks...
usbboot -v -c x1000 --addr 0xa0000000 --length 1048576 --upload mem00.bin
usbboot -v -c x1000 --addr 0xa0100000 --length 1048576 --upload mem01.bin
...
usbboot -v -c x1000 --addr 0xa1f00000 --length 1048576 --upload mem31.bin
cat mem*.bin > memory.bin
then spin up a quick program to go over memory.bin and verify that every word in memory got written with the intended value.
To move the flashing backlight (almost) as early as possible you can call spl_error() at the top of your bootloader's main function. You can also try changing the entry point in the linker script ingenic_x1000/app.lds. That should ensure spl_error() is the absolute first thing that runs.
#include "config.h"
#include "cpu.h"
OUTPUT_FORMAT("elf32-littlemips")
OUTPUT_ARCH(MIPS)
ENTRY(_start) /* <- change this to "main" in order to bypass crt0
(but be warned, this breaks C ABI because BSS segment won't be zeroed if you do this) */
STARTUP(target/mips/ingenic_x1000/crt0.o)
/* End of the audio buffer, where the codec buffer starts */
#define ENDAUDIOADDR (X1000_DRAM_END - PLUGIN_BUFFER_SIZE - CODEC_SIZE)
-
Hmm, if your DRAM contents are stable the issue might be elsewhere. I suspected DRAM because it's something very early on and not tested on your HW configuration. The only other DRAM-related thing which comes to mind is the addressing bits REG_DDRC_REMAPn, so you might try to confirm that the address mapping works by loop over all memory in the SPL like this:--
void spl_main(void)
{
/* Basic hardware init */
init();
uint32_t* ptr = (uint32_t*)0xa0000000;
unsigned count = 32 * 1024 * 1024 / 4;
for(unsigned i = 0; i < count; ++i)
*ptr++ = i;
return;
}
then grab the memory contents in 1M chunks...
usbboot -v -c x1000 --addr 0xa0000000 --length 1048576 --upload mem00.bin
usbboot -v -c x1000 --addr 0xa0100000 --length 1048576 --upload mem01.bin
...
usbboot -v -c x1000 --addr 0xa1f00000 --length 1048576 --upload mem31.bin
cat mem*.bin > memory.bin
then spin up a quick program to go over memory.bin and verify that every word in memory got written with the intended value.
Oh, you might be on to something here! If I understand the for loop correctly, we're going to write increasing values to every 8th address for 31 1024 blocks. However, I get an interesting pattern where it starts at 00 01 00 00, gets to ff 01 00 00, then does 00 01 00 00 - ff 01 00 00, then moves on to 00 03 00 00 - ff 03 00 00, so on and so forth... I think this might actually be indicative of a problem - is that right?
[root@localhost ingenic_tools]# ./usbboot -v -c x1000 -1 ../../build-erosqnative-boot/spl.erosq
Opening USB device a108:1000
Issue SET_DATA_ADDRESS 0xf4001000
Transfer 3596 bytes from host to device
Issue PROGRAM_START1 0xf4001800
[root@localhost ingenic_tools]# ./usbboot -v -c x1000 --addr 0xa0000000 --length 1048576 --upload mem00.bin
Opening USB device a108:1000
Issue SET_DATA_ADDRESS 0xa0000000
Issue SET_DATA_LENGTH 0x100000
[root@localhost ingenic_tools]# hexedit mem00.bin
00000000 00 01 00 00 01 01 00 00 02 01 00 00 03 01 00 00 04 01 00 00 05 01 00 00 06 01 00 00 07 01 00 00 ................................
00000020 08 01 00 00 09 01 00 00 0A 01 00 00 0B 01 00 00 0C 01 00 00 0D 01 00 00 0E 01 00 00 0F 01 00 00 ................................
00000040 10 01 00 00 11 01 00 00 12 01 00 00 13 01 00 00 14 01 00 00 15 01 00 00 16 01 00 00 17 01 00 00 ................................
00000060 18 01 00 00 19 01 00 00 1A 01 00 00 1B 01 00 00 1C 01 00 00 1D 01 00 00 1E 01 00 00 1F 01 00 00 ................................
00000080 20 01 00 00 21 01 00 00 22 01 00 00 23 01 00 00 24 01 00 00 25 01 00 00 26 01 00 00 27 01 00 00 ...!..."...#...$...%...&...'...
000000A0 28 01 00 00 29 01 00 00 2A 01 00 00 2B 01 00 00 2C 01 00 00 2D 01 00 00 2E 01 00 00 2F 01 00 00 (...)...*...+...,...-......./...
000000C0 30 01 00 00 31 01 00 00 32 01 00 00 33 01 00 00 34 01 00 00 35 01 00 00 36 01 00 00 37 01 00 00 0...1...2...3...4...5...6...7...
000000E0 38 01 00 00 39 01 00 00 3A 01 00 00 3B 01 00 00 3C 01 00 00 3D 01 00 00 3E 01 00 00 3F 01 00 00 8...9...:...;...<...=...>...?...
00000100 40 01 00 00 41 01 00 00 42 01 00 00 43 01 00 00 44 01 00 00 45 01 00 00 46 01 00 00 47 01 00 00 @...A...B...C...D...E...F...G...
00000120 48 01 00 00 49 01 00 00 4A 01 00 00 4B 01 00 00 4C 01 00 00 4D 01 00 00 4E 01 00 00 4F 01 00 00 H...I...J...K...L...M...N...O...
00000140 50 01 00 00 51 01 00 00 52 01 00 00 53 01 00 00 54 01 00 00 55 01 00 00 56 01 00 00 57 01 00 00 P...Q...R...S...T...U...V...W...
00000160 58 01 00 00 59 01 00 00 5A 01 00 00 5B 01 00 00 5C 01 00 00 5D 01 00 00 5E 01 00 00 5F 01 00 00 X...Y...Z...[...\...]...^..._...
00000180 60 01 00 00 61 01 00 00 62 01 00 00 63 01 00 00 64 01 00 00 65 01 00 00 66 01 00 00 67 01 00 00 `...a...b...c...d...e...f...g...
000001A0 68 01 00 00 69 01 00 00 6A 01 00 00 6B 01 00 00 6C 01 00 00 6D 01 00 00 6E 01 00 00 6F 01 00 00 h...i...j...k...l...m...n...o...
000001C0 70 01 00 00 71 01 00 00 72 01 00 00 73 01 00 00 74 01 00 00 75 01 00 00 76 01 00 00 77 01 00 00 p...q...r...s...t...u...v...w...
000001E0 78 01 00 00 79 01 00 00 7A 01 00 00 7B 01 00 00 7C 01 00 00 7D 01 00 00 7E 01 00 00 7F 01 00 00 x...y...z...{...|...}...~.......
00000200 80 01 00 00 81 01 00 00 82 01 00 00 83 01 00 00 84 01 00 00 85 01 00 00 86 01 00 00 87 01 00 00 ................................
00000220 88 01 00 00 89 01 00 00 8A 01 00 00 8B 01 00 00 8C 01 00 00 8D 01 00 00 8E 01 00 00 8F 01 00 00 ................................
00000240 90 01 00 00 91 01 00 00 92 01 00 00 93 01 00 00 94 01 00 00 95 01 00 00 96 01 00 00 97 01 00 00 ................................
00000260 98 01 00 00 99 01 00 00 9A 01 00 00 9B 01 00 00 9C 01 00 00 9D 01 00 00 9E 01 00 00 9F 01 00 00 ................................
00000280 A0 01 00 00 A1 01 00 00 A2 01 00 00 A3 01 00 00 A4 01 00 00 A5 01 00 00 A6 01 00 00 A7 01 00 00 ................................
000002A0 A8 01 00 00 A9 01 00 00 AA 01 00 00 AB 01 00 00 AC 01 00 00 AD 01 00 00 AE 01 00 00 AF 01 00 00 ................................
000002C0 B0 01 00 00 B1 01 00 00 B2 01 00 00 B3 01 00 00 B4 01 00 00 B5 01 00 00 B6 01 00 00 B7 01 00 00 ................................
000002E0 B8 01 00 00 B9 01 00 00 BA 01 00 00 BB 01 00 00 BC 01 00 00 BD 01 00 00 BE 01 00 00 BF 01 00 00 ................................
00000300 C0 01 00 00 C1 01 00 00 C2 01 00 00 C3 01 00 00 C4 01 00 00 C5 01 00 00 C6 01 00 00 C7 01 00 00 ................................
00000320 C8 01 00 00 C9 01 00 00 CA 01 00 00 CB 01 00 00 CC 01 00 00 CD 01 00 00 CE 01 00 00 CF 01 00 00 ................................
00000340 D0 01 00 00 D1 01 00 00 D2 01 00 00 D3 01 00 00 D4 01 00 00 D5 01 00 00 D6 01 00 00 D7 01 00 00 ................................
00000360 D8 01 00 00 D9 01 00 00 DA 01 00 00 DB 01 00 00 DC 01 00 00 DD 01 00 00 DE 01 00 00 DF 01 00 00 ................................
00000380 E0 01 00 00 E1 01 00 00 E2 01 00 00 E3 01 00 00 E4 01 00 00 E5 01 00 00 E6 01 00 00 E7 01 00 00 ................................
000003A0 E8 01 00 00 E9 01 00 00 EA 01 00 00 EB 01 00 00 EC 01 00 00 ED 01 00 00 EE 01 00 00 EF 01 00 00 ................................
000003C0 F0 01 00 00 F1 01 00 00 F2 01 00 00 F3 01 00 00 F4 01 00 00 F5 01 00 00 F6 01 00 00 F7 01 00 00 ................................
000003E0 F8 01 00 00 F9 01 00 00 FA 01 00 00 FB 01 00 00 FC 01 00 00 FD 01 00 00 FE 01 00 00 FF 01 00 00 ................................
00000400 00 01 00 00 01 01 00 00 02 01 00 00 03 01 00 00 04 01 00 00 05 01 00 00 06 01 00 00 07 01 00 00 ................................
00000420 08 01 00 00 09 01 00 00 0A 01 00 00 0B 01 00 00 0C 01 00 00 0D 01 00 00 0E 01 00 00 0F 01 00 00 ................................
00000440 10 01 00 00 11 01 00 00 12 01 00 00 13 01 00 00 14 01 00 00 15 01 00 00 16 01 00 00 17 01 00 00 ................................
00000460 18 01 00 00 19 01 00 00 1A 01 00 00 1B 01 00 00 1C 01 00 00 1D 01 00 00 1E 01 00 00 1F 01 00 00 ................................
00000480 20 01 00 00 21 01 00 00 22 01 00 00 23 01 00 00 24 01 00 00 25 01 00 00 26 01 00 00 27 01 00 00 ...!..."...#...$...%...&...'...
000004A0 28 01 00 00 29 01 00 00 2A 01 00 00 2B 01 00 00 2C 01 00 00 2D 01 00 00 2E 01 00 00 2F 01 00 00 (...)...*...+...,...-......./...
000004C0 30 01 00 00 31 01 00 00 32 01 00 00 33 01 00 00 34 01 00 00 35 01 00 00 36 01 00 00 37 01 00 00 0...1...2...3...4...5...6...7...
000004E0 38 01 00 00 39 01 00 00 3A 01 00 00 3B 01 00 00 3C 01 00 00 3D 01 00 00 3E 01 00 00 3F 01 00 00 8...9...:...;...<...=...>...?...
00000500 40 01 00 00 41 01 00 00 42 01 00 00 43 01 00 00 44 01 00 00 45 01 00 00 46 01 00 00 47 01 00 00 @...A...B...C...D...E...F...G...
00000520 48 01 00 00 49 01 00 00 4A 01 00 00 4B 01 00 00 4C 01 00 00 4D 01 00 00 4E 01 00 00 4F 01 00 00 H...I...J...K...L...M...N...O...
00000540 50 01 00 00 51 01 00 00 52 01 00 00 53 01 00 00 54 01 00 00 55 01 00 00 56 01 00 00 57 01 00 00 P...Q...R...S...T...U...V...W...
00000560 58 01 00 00 59 01 00 00 5A 01 00 00 5B 01 00 00 5C 01 00 00 5D 01 00 00 5E 01 00 00 5F 01 00 00 X...Y...Z...[...\...]...^..._...
00000580 60 01 00 00 61 01 00 00 62 01 00 00 63 01 00 00 64 01 00 00 65 01 00 00 66 01 00 00 67 01 00 00 `...a...b...c...d...e...f...g...
000005A0 68 01 00 00 69 01 00 00 6A 01 00 00 6B 01 00 00 6C 01 00 00 6D 01 00 00 6E 01 00 00 6F 01 00 00 h...i...j...k...l...m...n...o...
000005C0 70 01 00 00 71 01 00 00 72 01 00 00 73 01 00 00 74 01 00 00 75 01 00 00 76 01 00 00 77 01 00 00 p...q...r...s...t...u...v...w...
000005E0 78 01 00 00 79 01 00 00 7A 01 00 00 7B 01 00 00 7C 01 00 00 7D 01 00 00 7E 01 00 00 7F 01 00 00 x...y...z...{...|...}...~.......
00000600 80 01 00 00 81 01 00 00 82 01 00 00 83 01 00 00 84 01 00 00 85 01 00 00 86 01 00 00 87 01 00 00 ................................
00000620 88 01 00 00 89 01 00 00 8A 01 00 00 8B 01 00 00 8C 01 00 00 8D 01 00 00 8E 01 00 00 8F 01 00 00 ................................
00000640 90 01 00 00 91 01 00 00 92 01 00 00 93 01 00 00 94 01 00 00 95 01 00 00 96 01 00 00 97 01 00 00 ................................
00000660 98 01 00 00 99 01 00 00 9A 01 00 00 9B 01 00 00 9C 01 00 00 9D 01 00 00 9E 01 00 00 9F 01 00 00 ................................
00000680 A0 01 00 00 A1 01 00 00 A2 01 00 00 A3 01 00 00 A4 01 00 00 A5 01 00 00 A6 01 00 00 A7 01 00 00 ................................
000006A0 A8 01 00 00 A9 01 00 00 AA 01 00 00 AB 01 00 00 AC 01 00 00 AD 01 00 00 AE 01 00 00 AF 01 00 00 ................................
000006C0 B0 01 00 00 B1 01 00 00 B2 01 00 00 B3 01 00 00 B4 01 00 00 B5 01 00 00 B6 01 00 00 B7 01 00 00 ................................
000006E0 B8 01 00 00 B9 01 00 00 BA 01 00 00 BB 01 00 00 BC 01 00 00 BD 01 00 00 BE 01 00 00 BF 01 00 00 ................................
00000700 C0 01 00 00 C1 01 00 00 C2 01 00 00 C3 01 00 00 C4 01 00 00 C5 01 00 00 C6 01 00 00 C7 01 00 00 ................................
00000720 C8 01 00 00 C9 01 00 00 CA 01 00 00 CB 01 00 00 CC 01 00 00 CD 01 00 00 CE 01 00 00 CF 01 00 00 ................................
00000740 D0 01 00 00 D1 01 00 00 D2 01 00 00 D3 01 00 00 D4 01 00 00 D5 01 00 00 D6 01 00 00 D7 01 00 00 ................................
00000760 D8 01 00 00 D9 01 00 00 DA 01 00 00 DB 01 00 00 DC 01 00 00 DD 01 00 00 DE 01 00 00 DF 01 00 00 ................................
00000780 E0 01 00 00 E1 01 00 00 E2 01 00 00 E3 01 00 00 E4 01 00 00 E5 01 00 00 E6 01 00 00 E7 01 00 00 ................................
000007A0 E8 01 00 00 E9 01 00 00 EA 01 00 00 EB 01 00 00 EC 01 00 00 ED 01 00 00 EE 01 00 00 EF 01 00 00 ................................
000007C0 F0 01 00 00 F1 01 00 00 F2 01 00 00 F3 01 00 00 F4 01 00 00 F5 01 00 00 F6 01 00 00 F7 01 00 00 ................................
000007E0 F8 01 00 00 F9 01 00 00 FA 01 00 00 FB 01 00 00 FC 01 00 00 FD 01 00 00 FE 01 00 00 FF 01 00 00 ................................
00000800 00 03 00 00 01 03 00 00 02 03 00 00 03 03 00 00 04 03 00 00 05 03 00 00 06 03 00 00 07 03 00 00 ................................
00000820 08 03 00 00 09 03 00 00 0A 03 00 00 0B 03 00 00 0C 03 00 00 0D 03 00 00 0E 03 00 00 0F 03 00 00 ................................
00000840 10 03 00 00 11 03 00 00 12 03 00 00 13 03 00 00 14 03 00 00 15 03 00 00 16 03 00 00 17 03 00 00 ................................
00000860 18 03 00 00 19 03 00 00 1A 03 00 00 1B 03 00 00 1C 03 00 00 1D 03 00 00 1E 03 00 00 1F 03 00 00 ................................
00000880 20 03 00 00 21 03 00 00 22 03 00 00 23 03 00 00 24 03 00 00 25 03 00 00 26 03 00 00 27 03 00 00 ...!..."...#...$...%...&...'...
000008A0 28 03 00 00 29 03 00 00 2A 03 00 00 2B 03 00 00 2C 03 00 00 2D 03 00 00 2E 03 00 00 2F 03 00 00 (...)...*...+...,...-......./...
000008C0 30 03 00 00 31 03 00 00 32 03 00 00 33 03 00 00 34 03 00 00 35 03 00 00 36 03 00 00 37 03 00 00 0...1...2...3...4...5...6...7...
000008E0 38 03 00 00 39 03 00 00 3A 03 00 00 3B 03 00 00 3C 03 00 00 3D 03 00 00 3E 03 00 00 3F 03 00 00 8...9...:...;...<...=...>...?...
00000900 40 03 00 00 41 03 00 00 42 03 00 00 43 03 00 00 44 03 00 00 45 03 00 00 46 03 00 00 47 03 00 00 @...A...B...C...D...E...F...G...
00000920 48 03 00 00 49 03 00 00 4A 03 00 00 4B 03 00 00 4C 03 00 00 4D 03 00 00 4E 03 00 00 4F 03 00 00 H...I...J...K...L...M...N...O...
00000940 50 03 00 00 51 03 00 00 52 03 00 00 53 03 00 00 54 03 00 00 55 03 00 00 56 03 00 00 57 03 00 00 P...Q...R...S...T...U...V...W...
00000960 58 03 00 00 59 03 00 00 5A 03 00 00 5B 03 00 00 5C 03 00 00 5D 03 00 00 5E 03 00 00 5F 03 00 00 X...Y...Z...[...\...]...^..._...
00000980 60 03 00 00 61 03 00 00 62 03 00 00 63 03 00 00 64 03 00 00 65 03 00 00 66 03 00 00 67 03 00 00 `...a...b...c...d...e...f...g...
000009A0 68 03 00 00 69 03 00 00 6A 03 00 00 6B 03 00 00 6C 03 00 00 6D 03 00 00 6E 03 00 00 6F 03 00 00 h...i...j...k...l...m...n...o...
000009C0 70 03 00 00 71 03 00 00 72 03 00 00 73 03 00 00 74 03 00 00 75 03 00 00 76 03 00 00 77 03 00 00 p...q...r...s...t...u...v...w...
000009E0 78 03 00 00 79 03 00 00 7A 03 00 00 7B 03 00 00 7C 03 00 00 7D 03 00 00 7E 03 00 00 7F 03 00 00 x...y...z...{...|...}...~.......
00000A00 80 03 00 00 81 03 00 00 82 03 00 00 83 03 00 00 84 03 00 00 85 03 00 00 86 03 00 00 87 03 00 00 ................................
00000A20 88 03 00 00 89 03 00 00 8A 03 00 00 8B 03 00 00 8C 03 00 00 8D 03 00 00 8E 03 00 00 8F 03 00 00 ................................
00000A40 90 03 00 00 91 03 00 00 92 03 00 00 93 03 00 00 94 03 00 00 95 03 00 00 96 03 00 00 97 03 00 00 ................................
00000A60 98 03 00 00 99 03 00 00 9A 03 00 00 9B 03 00 00 9C 03 00 00 9D 03 00 00 9E 03 00 00 9F 03 00 00 ................................
00000A80 A0 03 00 00 A1 03 00 00 A2 03 00 00 A3 03 00 00 A4 03 00 00 A5 03 00 00 A6 03 00 00 A7 03 00 00 ................................
00000AA0 A8 03 00 00 A9 03 00 00 AA 03 00 00 AB 03 00 00 AC 03 00 00 AD 03 00 00 AE 03 00 00 AF 03 00 00 ................................
00000AC0 B0 03 00 00 B1 03 00 00 B2 03 00 00 B3 03 00 00 B4 03 00 00 B5 03 00 00 B6 03 00 00 B7 03 00 00 ................................
00000AE0 B8 03 00 00 B9 03 00 00 BA 03 00 00 BB 03 00 00 BC 03 00 00 BD 03 00 00 BE 03 00 00 BF 03 00 00 ................................
00000B00 C0 03 00 00 C1 03 00 00 C2 03 00 00 C3 03 00 00 C4 03 00 00 C5 03 00 00 C6 03 00 00 C7 03 00 00 ................................
00000B20 C8 03 00 00 C9 03 00 00 CA 03 00 00 CB 03 00 00 CC 03 00 00 CD 03 00 00 CE 03 00 00 CF 03 00 00 ................................
00000B40 D0 03 00 00 D1 03 00 00 D2 03 00 00 D3 03 00 00 D4 03 00 00 D5 03 00 00 D6 03 00 00 D7 03 00 00 ................................
00000B60 D8 03 00 00 D9 03 00 00 DA 03 00 00 DB 03 00 00 DC 03 00 00 DD 03 00 00 DE 03 00 00 DF 03 00 00 ................................
00000B80 E0 03 00 00 E1 03 00 00 E2 03 00 00 E3 03 00 00 E4 03 00 00 E5 03 00 00 E6 03 00 00 E7 03 00 00 ................................
00000BA0 E8 03 00 00 E9 03 00 00 EA 03 00 00 EB 03 00 00 EC 03 00 00 ED 03 00 00 EE 03 00 00 EF 03 00 00 ................................
00000BC0 F0 03 00 00 F1 03 00 00 F2 03 00 00 F3 03 00 00 F4 03 00 00 F5 03 00 00 F6 03 00 00 F7 03 00 00 ................................
00000BE0 F8 03 00 00 F9 03 00 00 FA 03 00 00 FB 03 00 00 FC 03 00 00 FD 03 00 00 FE 03 00 00 FF 03 00 00 ................................
00000C00 00 03 00 00 01 03 00 00 02 03 00 00 03 03 00 00 04 03 00 00 05 03 00 00 06 03 00 00 07 03 00 00 ................................
00000C20 08 03 00 00 09 03 00 00 0A 03 00 00 0B 03 00 00 0C 03 00 00 0D 03 00 00 0E 03 00 00 0F 03 00 00 ................................
00000C40 10 03 00 00 11 03 00 00 12 03 00 00 13 03 00 00 14 03 00 00 15 03 00 00 16 03 00 00 17 03 00 00 ................................
00000C60 18 03 00 00 19 03 00 00 1A 03 00 00 1B 03 00 00 1C 03 00 00 1D 03 00 00 1E 03 00 00 1F 03 00 00 ................................
00000C80 20 03 00 00 21 03 00 00 22 03 00 00 23 03 00 00 24 03 00 00 25 03 00 00 26 03 00 00 27 03 00 00 ...!..."...#...$...%...&...'...
00000CA0 28 03 00 00 29 03 00 00 2A 03 00 00 2B 03 00 00 2C 03 00 00 2D 03 00 00 2E 03 00 00 2F 03 00 00 (...)...*...+...,...-......./...
00000CC0 30 03 00 00 31 03 00 00 32 03 00 00 33 03 00 00 34 03 00 00 35 03 00 00 36 03 00 00 37 03 00 00 0...1...2...3...4...5...6...7...
00000CE0 38 03 00 00 39 03 00 00 3A 03 00 00 3B 03 00 00 3C 03 00 00 3D 03 00 00 3E 03 00 00 3F 03 00 00 8...9...:...;...<...=...>...?...
00000D00 40 03 00 00 41 03 00 00 42 03 00 00 43 03 00 00 44 03 00 00 45 03 00 00 46 03 00 00 47 03 00 00 @...A...B...C...D...E...F...G...
00000D20 48 03 00 00 49 03 00 00 4A 03 00 00 4B 03 00 00 4C 03 00 00 4D 03 00 00 4E 03 00 00 4F 03 00 00 H...I...J...K...L...M...N...O...
00000D40 50 03 00 00 51 03 00 00 52 03 00 00 53 03 00 00 54 03 00 00 55 03 00 00 56 03 00 00 57 03 00 00 P...Q...R...S...T...U...V...W...
00000D60 58 03 00 00 59 03 00 00 5A 03 00 00 5B 03 00 00 5C 03 00 00 5D 03 00 00 5E 03 00 00 5F 03 00 00 X...Y...Z...[...\...]...^..._...
So on and so forth... Almost as if it's writing 00 00 00 00 - ff 00 00 00 00, overwriting it with 00 01 00 00 - ff 01 00 00, before moving on to the actual next block for 00 02 00 00 (and overwriting that then, too). - No wait, that's not quite right...
(I did try changing the unsigned types to explicitly uint32_t too, no change.)
-
0x2c2c2c2c is most definitely the value pointed to by smart_config.write_gram_cmd. 0x2c is the standard write_memory_start command of MIPI DCS, used to initiate writing data to the LCD controller's framebuffer RAM. It's not fb_videomode.flag.
In the M3K kernel sources the init_pixclock member is dropped vs. the YuanhuanLiang repo. That's probably why your fb_videomode struct is too big. Maybe you should check the M3K sources for other structs that seem "wrong", it's possible that the Eros Q is using sources similar to the M3K's. (A reminder that your source won't necessarily line up with the binary!)
So with this in mind, jzfb stuff looks lined up right to me:
Code: [Select]
805147ac = bitfield
clkply_active_rising=1
newcfg_fmt_conv=1
others=0
(this is standard fare, nothing exotic)
805147b8 = smart_config.bus_width
805147c0 = smart_config.length_data_table
805147c4 = smart_config.data_table
Also a look at this
Code: [Select]
805147a4 00 uint:1 0h pinmd:1
805147a4 00 uint:1 0h pixclk_falli
805147a4 00 uint:1 0h data_enable_
805147a5 00 00 00 00 00 smart_co field_0x1d
That start address 805147a5 is almost certainly wrong. The alignment of the struct is at least 4 bytes and struct alignment = maximum alignment of any member. Plus, the first member must be aligned wrong because enums seem to be ints under the descargar kmspico gratis (https://www.webnovedad.com/descargar-kmspico-pc/) used (an implementation detail which I haven't bothered to verify but seems to be true).
Weirdly, Ghidra doesn't align structs by default -- in the lower right corner of the struct editor is an unobtrusive "Align" checkbox. Checking that will make Ghidra insert the correct padding to follow C alignment rules (afaict). It seems Ghidra defaults to "packed" alignment by default... it confused me for a couple hours too. :o
In this particular case I think I can say with certainty the alignment is wrong -- but in general, you would need to check the generated code to see if the compiler emitted code to access aligned addresses or unaligned addresses. The lw/sw instructions to load/store a word require 4 byte aligned addresses, whereas to access an improperly aligned word, the compiler would need to generate different instructions -- in the case of MIPS the instructions used would depend on the ISA version targeted, IIRC the older versions don't have any unaligned load/store instructions.
Quote from: dconrad on June 19, 2021, 10:38:06 PM
I have a suspicion that the jzfb_platform_data struct is actually the one outlined in arch/mips/xburst/soc-x1000/include/mach/jzfb.h, could this be true?
That's my assumption. Sure, it's defined in other parts of the xburst tree for other SoCs, but this is an X1000 kernel... where else would it be defined?
Hi
You can reconfigurate kernel with:
zcat /proc/config.gz > .config