├── crc.h ├── gff.h ├── Makefile ├── i2c.h ├── crc.cpp ├── README.md ├── i2clinux.cpp ├── gff.cpp └── main.cpp /crc.h: -------------------------------------------------------------------------------- 1 | #ifndef __CRC_H__ 2 | #define __CRC_H__ 3 | 4 | #include 5 | 6 | void InitCRC(); 7 | void ProcessCRC(const uint8_t *data, int len); 8 | uint8_t GetCRC(); 9 | 10 | #endif /* __CRC_H__ */ 11 | 12 | -------------------------------------------------------------------------------- /gff.h: -------------------------------------------------------------------------------- 1 | #ifndef __GFF_H__ 2 | #define __GFF_H__ 3 | 4 | #include 5 | 6 | uint32_t ComputeGffDecodedSize(uint8_t* data_ptr, uint32_t data_len); 7 | bool DecodeGff(uint8_t* data_ptr, uint32_t data_len, uint8_t* dest); 8 | 9 | #endif /* __GFF_H__ */ 10 | 11 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | rtd_prog: main.cpp crc.o gff.o i2c.o 2 | ${CXX} ${CPPFLAGS} ${CXXFLAGS} ${LDFLAGS} crc.o gff.o i2c.o main.cpp -o rtd_prog 3 | 4 | crc.o: crc.cpp crc.h 5 | ${CXX} ${CPPFLAGS} ${CXXFLAGS} -c -o crc.o crc.cpp 6 | 7 | gff.o: gff.cpp gff.h 8 | ${CXX} ${CPPFLAGS} ${CXXFLAGS} -c -o gff.o gff.cpp 9 | 10 | i2c.o: i2clinux.cpp i2c.h 11 | ${CXX} ${CPPFLAGS} ${CXXFLAGS} -c -o i2c.o i2clinux.cpp 12 | 13 | clean: 14 | rm -f *.o rtd_prog 15 | -------------------------------------------------------------------------------- /i2c.h: -------------------------------------------------------------------------------- 1 | #ifndef __I2C_H__ 2 | #define __I2C_H__ 3 | 4 | #include 5 | 6 | bool InitI2C(int adapter_nr); 7 | bool CloseI2C(); 8 | 9 | bool SetI2CAddr(uint8_t addr); 10 | 11 | bool WriteReg(uint8_t reg, uint8_t value); 12 | uint8_t ReadReg(uint8_t reg); 13 | int32_t ReadBytesFromAddr(uint8_t reg, uint8_t* dest, uint8_t len); 14 | bool WriteBytesToAddr(uint8_t reg, uint8_t* values, uint8_t len); 15 | 16 | #endif /* __I2C_H__ */ 17 | 18 | -------------------------------------------------------------------------------- /crc.cpp: -------------------------------------------------------------------------------- 1 | #include "crc.h" 2 | 3 | static unsigned gCrc = 0; 4 | 5 | void InitCRC() { 6 | gCrc = 0; 7 | } 8 | 9 | void ProcessCRC(const uint8_t *data, int len) 10 | { 11 | int i, j; 12 | for (j = len; j; j--, data++) { 13 | gCrc ^= (*data << 8); 14 | for(i = 8; i; i--) { 15 | if (gCrc & 0x8000) 16 | gCrc ^= (0x1070 << 3); 17 | gCrc <<= 1; 18 | } 19 | } 20 | } 21 | 22 | uint8_t GetCRC() { 23 | return (uint8_t)(gCrc >> 8); 24 | } 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rtd266x_programmer 2 | Programmer for RTD266X using linux and i2c-dev 3 | 4 | This allows you to program these devices just using a VGA cable plugged in to your graphics card (it uses the I2C bus on the DDC pins). No need to buy a programmer! 5 | 6 | This is based on the code [here](https://github.com/ghent360/RTD-2660-Programmer) (but that code uses an external USB I2C interface and MS windows). 7 | 8 | Building 9 | -------- 10 | No special requirements except `i2c-dev.h`, and you probably want `i2cdetect` from `i2c-tools` as well. On ubuntu you just need to do `sudo apt-get install i2c-tools libi2c-dev` and that's it. Then just run `make`. 11 | 12 | Using it 13 | -------- 14 | 15 | Make sure to `modprobe i2c-dev` 16 | 17 | Then run `i2cdetect -l`. I get the following output: 18 | 19 | i2c-0 i2c i915 gmbus ssc I2C adapter 20 | i2c-1 i2c i915 gmbus vga I2C adapter 21 | i2c-2 i2c i915 gmbus panel I2C adapter 22 | i2c-3 i2c i915 gmbus dpc I2C adapter 23 | i2c-4 i2c i915 gmbus dpb I2C adapter 24 | i2c-5 i2c i915 gmbus dpd I2C adapter 25 | i2c-6 i2c DPDDC-B I2C adapter 26 | i2c-7 i2c DPDDC-C I2C adapter 27 | i2c-8 i2c DPDDC-D I2C adapter 28 | 29 | Clearly `i2c-1` is the i2c bus connected to the VGA DDC pins. Now I can plug a VGA cable between my laptop and the RTD2660 board and run `rtd_prog -d 1 -b backup.bin` to backup my firmware. 30 | 31 | Usage: rtd_prog -d \ [-option[s]] 32 | Options: 33 | -d \ : Specify the i2c bus to use, e.g. -d 1 means /dev/i2c-1 34 | -b \ : Backup the current firmware on the RTD266X to a file 35 | -f \ : Flash a file to the RTD266X 36 | 37 | -------------------------------------------------------------------------------- /i2clinux.cpp: -------------------------------------------------------------------------------- 1 | #include "i2c.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | extern "C" 7 | { 8 | #include 9 | } 10 | 11 | static int file; 12 | static char filename[20]; 13 | 14 | bool 15 | InitI2C(int adapter_nr) { 16 | snprintf(filename, 19, "/dev/i2c-%d", adapter_nr); 17 | file = open(filename, O_RDWR); 18 | if (file < 0) { 19 | /* ERROR HANDLING; you can check errno to see what went wrong */ 20 | fprintf(stderr, "Error: could not open %s\n", filename); 21 | return false; 22 | } 23 | return true; 24 | } 25 | 26 | bool 27 | CloseI2C() { 28 | if (close(file) < 0) { 29 | fprintf(stderr, "Error: could not close %s\n", filename); 30 | return false; 31 | } 32 | return true; 33 | } 34 | 35 | bool 36 | SetI2CAddr(uint8_t addr) { 37 | if (ioctl(file, I2C_SLAVE, addr) < 0) { 38 | /* ERROR HANDLING; you can check errno to see what went wrong */ 39 | fprintf(stderr, "Error: could not set slave address to %x\n", addr); 40 | return false; 41 | } 42 | return true; 43 | } 44 | 45 | bool 46 | WriteReg(uint8_t reg, uint8_t value) { 47 | if (i2c_smbus_write_byte_data(file, reg, value) < 0) { 48 | /* fprintf(stderr, "Error: could not write to register %d\n", reg); */ 49 | return false; 50 | } 51 | return true; 52 | } 53 | 54 | uint8_t 55 | ReadReg(uint8_t reg) { 56 | int32_t result; 57 | result = i2c_smbus_read_byte_data(file, reg); 58 | /* if ((result = i2c_smbus_read_byte_data(file, reg)) == -1) 59 | fprintf(stderr, "Error: could not read from register %d\n", reg); */ 60 | 61 | return (uint8_t)result; 62 | } 63 | 64 | int32_t 65 | ReadBytesFromAddr(uint8_t reg, uint8_t* dest, uint8_t len) { 66 | int read; 67 | if ((read = i2c_smbus_read_i2c_block_data(file, reg, len, dest)) == -1) { 68 | /* fprintf(stderr, "Error: could not read block from register %d\n", reg); */ 69 | return -1; 70 | } 71 | return read; 72 | } 73 | 74 | bool 75 | WriteBytesToAddr(uint8_t reg, uint8_t* values, uint8_t len) { 76 | //if (i2c_smbus_write_block_data(file, reg, len, values) < 0) { 77 | if (i2c_smbus_write_i2c_block_data(file, reg, len, values) < 0) { 78 | /* fprintf(stderr, "Error: could not write block data to register %d\n", reg); */ 79 | return false; 80 | } 81 | return true; 82 | } 83 | 84 | -------------------------------------------------------------------------------- /gff.cpp: -------------------------------------------------------------------------------- 1 | #include "gff.h" 2 | 3 | class CBitStream { 4 | public: 5 | CBitStream(uint8_t* data_ptr, uint32_t data_len) 6 | : mask_(0x80), 7 | data_ptr_(data_ptr), 8 | data_len_(data_len) {} 9 | 10 | bool HasData() const { return mask_ != 0 || data_len_ != 0; } 11 | uint32_t DataSize() const { return data_len_; } 12 | bool ReadBit() { 13 | if (!mask_) { 14 | NextMask(); 15 | } 16 | bool result = (*data_ptr_ & mask_) != 0; 17 | mask_ >>= 1; 18 | return result; 19 | } 20 | 21 | private: 22 | void NextMask() { 23 | if (data_len_) { 24 | mask_ = 0x80; 25 | data_ptr_++; 26 | data_len_--; 27 | } 28 | } 29 | 30 | uint8_t mask_; 31 | uint8_t* data_ptr_; 32 | uint32_t data_len_; 33 | }; 34 | 35 | static uint8_t gff_decode_nibble(CBitStream* bit_stream) { 36 | uint8_t zero_cnt = 0; 37 | bool bit; 38 | 39 | do { 40 | if (!bit_stream->HasData()) return 0xf0; 41 | 42 | bit = bit_stream->ReadBit(); 43 | if (bit) break; 44 | zero_cnt++; 45 | } while (zero_cnt < 6); 46 | 47 | if (zero_cnt > 5) { 48 | if (bit_stream->DataSize() == 1) { 49 | return 0xf0; 50 | } 51 | return 0xff; 52 | } 53 | 54 | switch (zero_cnt) { 55 | case 0: 56 | return 0; 57 | 58 | case 1: 59 | bit = bit_stream->ReadBit(); 60 | return (bit) ? 0xf : 1; 61 | 62 | case 2: 63 | bit = bit_stream->ReadBit(); 64 | return (bit) ? 8 : 2; 65 | 66 | case 3: 67 | bit = bit_stream->ReadBit(); 68 | return (bit) ? 7 : 0xc; 69 | 70 | case 4: 71 | bit = bit_stream->ReadBit(); 72 | if (bit) { 73 | bit = bit_stream->ReadBit(); 74 | return (bit) ? 9 : 4; 75 | } else { 76 | bit = bit_stream->ReadBit(); 77 | if (bit) { 78 | bit = bit_stream->ReadBit(); 79 | return (bit) ? 5 : 0xa; 80 | } else { 81 | bit = bit_stream->ReadBit(); 82 | return (bit) ? 0xb : 3; 83 | } 84 | } 85 | break; 86 | 87 | case 5: 88 | bit = bit_stream->ReadBit(); 89 | if (bit) { 90 | bit = bit_stream->ReadBit(); 91 | return (bit) ? 0xd : 0xe; 92 | } else { 93 | bit = bit_stream->ReadBit(); 94 | return (bit) ? 6 : 0xff; 95 | } 96 | break; 97 | } 98 | return 0xff; 99 | } 100 | 101 | uint32_t ComputeGffDecodedSize(uint8_t* data_ptr, uint32_t data_len) { 102 | CBitStream bs(data_ptr, data_len); 103 | uint32_t cnt = 0; 104 | while (bs.HasData()) { 105 | uint8_t b = gff_decode_nibble(&bs); 106 | if (b == 0xff) return 0; 107 | if (b == 0xf0) return cnt; // End of file 108 | 109 | b = gff_decode_nibble(&bs); 110 | if (b > 0xf) return 0; // Error or odd number of nibbles 111 | cnt++; 112 | } 113 | return cnt; 114 | } 115 | 116 | bool DecodeGff(uint8_t* data_ptr, uint32_t data_len, uint8_t* dest) { 117 | CBitStream bs(data_ptr, data_len); 118 | while (bs.HasData()) { 119 | uint8_t n1 = gff_decode_nibble(&bs); 120 | if (n1 == 0xf0) return true; // End of file 121 | if (n1 == 0xff) return false; 122 | 123 | uint8_t n2 = gff_decode_nibble(&bs); 124 | if (n2 > 0xf) return false; 125 | 126 | uint8_t byte = (n1 << 4) | n2; 127 | *dest++ = byte; 128 | } 129 | return true; 130 | } 131 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | // main.cpp : Defines the entry point for the console application. 2 | // 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "crc.h" 8 | #include "i2c.h" 9 | #include "gff.h" 10 | 11 | struct FlashDesc { 12 | const char* device_name; 13 | uint32_t jedec_id; 14 | uint32_t size_kb; 15 | uint32_t page_size; 16 | uint32_t block_size_kb; 17 | }; 18 | 19 | static const FlashDesc FlashDevices[] = { 20 | // name, Jedec ID, sizeK, page size, block sizeK 21 | {"AT25DF041A" , 0x1F4401, 512, 256, 64}, 22 | {"AT25DF161" , 0x1F4602, 2 * 1024, 256, 64}, 23 | {"AT26DF081A" , 0x1F4501, 1 * 1024, 256, 64}, 24 | {"AT26DF0161" , 0x1F4600, 2 * 1024, 256, 64}, 25 | {"AT26DF161A" , 0x1F4601, 2 * 1024, 256, 64}, 26 | {"AT25DF321" , 0x1F4701, 4 * 1024, 256, 64}, 27 | {"AT25DF512B" , 0x1F6501, 64, 256, 32}, 28 | {"AT25DF512B" , 0x1F6500, 64, 256, 32}, 29 | {"AT25DF021" , 0x1F3200, 256, 256, 64}, 30 | {"AT26DF641" , 0x1F4800, 8 * 1024, 256, 64}, 31 | // Manufacturer: ST 32 | {"M25P05" , 0x202010, 64, 256, 32}, 33 | {"M25P10" , 0x202011, 128, 256, 32}, 34 | {"M25P20" , 0x202012, 256, 256, 64}, 35 | {"M25P40" , 0x202013, 512, 256, 64}, 36 | {"M25P80" , 0x202014, 1 * 1024, 256, 64}, 37 | {"M25P16" , 0x202015, 2 * 1024, 256, 64}, 38 | {"M25P32" , 0x202016, 4 * 1024, 256, 64}, 39 | {"M25P64" , 0x202017, 8 * 1024, 256, 64}, 40 | // Manufacturer: Windbond 41 | {"W25X10" , 0xEF3011, 128, 256, 64}, 42 | {"W25X20" , 0xEF3012, 256, 256, 64}, 43 | {"W25X40" , 0xEF3013, 512, 256, 64}, 44 | {"W25X80" , 0xEF3014, 1 * 1024, 256, 64}, 45 | // Manufacturer: Macronix 46 | {"MX25L512" , 0xC22010, 64, 256, 64}, 47 | {"MX25L3205" , 0xC22016, 4 * 1024, 256, 64}, 48 | {"MX25L6405" , 0xC22017, 8 * 1024, 256, 64}, 49 | {"MX25L8005" , 0xC22014, 1024, 256, 64}, 50 | // Microchip 51 | {"SST25VF512" , 0xBF4800, 64, 256, 32}, 52 | {"SST25VF032" , 0xBF4A00, 4 * 1024, 256, 32}, 53 | {"MX25L4005" , 0xC22013, 1024, 256, 64}, 54 | {NULL , 0, 0, 0, 0} 55 | }; 56 | 57 | enum ECommondCommandType { 58 | E_CC_NOOP = 0, 59 | E_CC_WRITE = 1, 60 | E_CC_READ = 2, 61 | E_CC_WRITE_AFTER_WREN = 3, 62 | E_CC_WRITE_AFTER_EWSR = 4, 63 | E_CC_ERASE = 5 64 | }; 65 | 66 | uint32_t SPICommonCommand(ECommondCommandType cmd_type, 67 | uint8_t cmd_code, 68 | uint8_t num_reads, 69 | uint8_t num_writes, 70 | uint32_t write_value) { 71 | num_reads &= 3; 72 | num_writes &= 3; 73 | write_value &= 0xFFFFFF; 74 | uint8_t reg_value = (cmd_type << 5) | 75 | (num_writes << 3) | 76 | (num_reads << 1); 77 | 78 | WriteReg(0x60, reg_value); 79 | WriteReg(0x61, cmd_code); 80 | switch (num_writes) { 81 | case 3: 82 | WriteReg(0x64, write_value >> 16); 83 | WriteReg(0x65, write_value >> 8); 84 | WriteReg(0x66, write_value); 85 | break; 86 | case 2: 87 | WriteReg(0x64, write_value >> 8); 88 | WriteReg(0x65, write_value); 89 | break; 90 | case 1: 91 | WriteReg(0x64, write_value); 92 | break; 93 | } 94 | WriteReg(0x60, reg_value | 1); // Execute the command 95 | uint8_t b; 96 | do { 97 | b = ReadReg(0x60); 98 | } while (b & 1); // TODO: add timeout and reset the controller 99 | switch (num_reads) { 100 | case 0: return 0; 101 | case 1: return ReadReg(0x67); 102 | case 2: return (ReadReg(0x67) << 8) | ReadReg(0x68); 103 | case 3: return (ReadReg(0x67) << 16) | (ReadReg(0x68) << 8) | ReadReg(0x69); 104 | } 105 | return 0; 106 | } 107 | 108 | void SPIRead(uint32_t address, uint8_t *data, int32_t len) { 109 | WriteReg(0x60, 0x46); 110 | WriteReg(0x61, 0x3); 111 | WriteReg(0x64, address>>16); 112 | WriteReg(0x65, address>>8); 113 | WriteReg(0x66, address); 114 | WriteReg(0x60, 0x47); // Execute the command 115 | uint8_t b; 116 | do { 117 | b = ReadReg(0x60); 118 | } while (b & 1); // TODO: add timeout and reset the controller 119 | while (len > 0) { 120 | int32_t read_len = len, actual = 0; 121 | if (read_len > 32) 122 | read_len = 32; 123 | actual = ReadBytesFromAddr(0x70, data, read_len); 124 | data += actual;//read_len; 125 | len -= actual;//read_len; 126 | } 127 | } 128 | 129 | void PrintManufacturer(uint32_t id) { 130 | switch (id) { 131 | case 0x20: printf("ST"); break; 132 | case 0xef: printf("Winbond"); break; 133 | case 0x1f: printf("Atmel"); break; 134 | case 0xc2: printf("Macronix"); break; 135 | case 0xbf: printf("Microchip"); break; 136 | default: printf("Unknown");break; 137 | } 138 | } 139 | 140 | static const FlashDesc* FindChip(uint32_t jedec_id) { 141 | const FlashDesc* chip = FlashDevices; 142 | while (chip->jedec_id != 0) { 143 | if (chip->jedec_id == jedec_id) 144 | return chip; 145 | chip++; 146 | } 147 | return NULL; 148 | } 149 | 150 | uint8_t SPIComputeCRC(uint32_t start, uint32_t end) { 151 | WriteReg(0x64, start >> 16); 152 | WriteReg(0x65, start >> 8); 153 | WriteReg(0x66, start); 154 | 155 | WriteReg(0x72, end >> 16); 156 | WriteReg(0x73, end >> 8); 157 | WriteReg(0x74, end); 158 | 159 | WriteReg(0x6f, 0x84); 160 | uint8_t b; 161 | do 162 | { 163 | b = ReadReg(0x6f); 164 | } while (!(b & 0x2)); // TODO: add timeout and reset the controller 165 | return ReadReg(0x75); 166 | } 167 | 168 | uint8_t GetManufacturerId(uint32_t jedec_id) { 169 | return jedec_id >> 16; 170 | } 171 | 172 | void SetupChipCommands(uint32_t jedec_id) { 173 | uint8_t manufacturer_id = GetManufacturerId(jedec_id); 174 | switch (manufacturer_id) { 175 | case 0xEF: 176 | case 0xC2: 177 | // These are the codes for Winbond 178 | WriteReg(0x62, 0x6); // Flash Write enable op code 179 | WriteReg(0x63, 0x50); // Flash Write register op code 180 | WriteReg(0x6a, 0x3); // Flash Read op code. 181 | WriteReg(0x6b, 0xb); // Flash Fast read op code. 182 | WriteReg(0x6d, 0x2); // Flash program op code. 183 | WriteReg(0x6e, 0x5); // Flash read status op code. 184 | break; 185 | default: 186 | printf("Can not handle manufacturer code %02x\n", manufacturer_id); 187 | exit(-6); 188 | break; 189 | } 190 | } 191 | 192 | bool SaveFlash(const char *output_file_name, uint32_t chip_size) { 193 | FILE *dump = fopen(output_file_name, "wb"); 194 | uint32_t addr = 0; 195 | InitCRC(); 196 | do { 197 | uint8_t buffer[1024]; 198 | printf("Reading addr %x\r", addr); 199 | SPIRead(addr, buffer, sizeof(buffer)); 200 | fwrite(buffer, 1, sizeof(buffer), dump); 201 | ProcessCRC(buffer, sizeof(buffer)); 202 | addr += sizeof(buffer); 203 | } while (addr < chip_size); 204 | printf("done.\n"); 205 | fclose(dump); 206 | uint8_t data_crc = GetCRC(); 207 | uint8_t chip_crc = SPIComputeCRC(0, chip_size - 1); 208 | printf("Received data CRC %02x\n", data_crc); 209 | printf("Chip CRC %02x\n", chip_crc); 210 | return data_crc == chip_crc; 211 | } 212 | 213 | uint64_t GetFileSize(FILE* file) { 214 | uint64_t current_pos; 215 | uint64_t result; 216 | current_pos = ftell(file); 217 | fseek(file, 0, SEEK_END); 218 | result = ftell(file); 219 | fseek(file, current_pos, SEEK_SET); 220 | return result; 221 | } 222 | 223 | static uint8_t* ReadFile(const char *file_name, uint32_t* size) { 224 | FILE *file = fopen(file_name, "rb"); 225 | uint8_t* result = NULL; 226 | if (NULL == file) { 227 | printf("Can't open input file %s\n", file_name); 228 | return result; 229 | } 230 | uint64_t file_size64 = GetFileSize(file); 231 | if (file_size64 > 8*1024*1024) { 232 | printf("This file looks too big %lu \n", file_size64); 233 | fclose(file); 234 | return result; 235 | } 236 | uint32_t file_size = (uint32_t)file_size64; 237 | result = new uint8_t[file_size]; 238 | if (NULL == result) { 239 | printf("Not enough RAM.\n"); 240 | fclose(file); 241 | return result; 242 | } 243 | fread(result, 1, file_size, file); 244 | fclose(file); 245 | if (memcmp("GMI GFF V1.0", result, 12) == 0) { 246 | printf("Detected GFF image.\n"); 247 | // Handle GFF file 248 | if (file_size < 256) { 249 | printf("This file looks to small %d\n", file_size); 250 | delete [] result; 251 | return NULL; 252 | } 253 | uint32_t gff_size = ComputeGffDecodedSize(result + 256, 254 | file_size - 256); 255 | if (gff_size == 0) { 256 | printf("GFF Decoding failed for this file\n"); 257 | delete [] result; 258 | return NULL; 259 | } 260 | uint8_t* gff_data = new uint8_t[gff_size]; 261 | if (NULL == gff_data) { 262 | printf("Not enough RAM.\n"); 263 | delete [] result; 264 | return NULL; 265 | } 266 | DecodeGff(result + 256, file_size - 256, gff_data); 267 | // Replace the encoded buffer with the decoded data. 268 | delete [] result; 269 | result = gff_data; 270 | file_size = gff_size; 271 | } 272 | if (NULL != size) { 273 | *size = file_size; 274 | } 275 | return result; 276 | } 277 | 278 | static bool ShouldProgramPage(uint8_t* buffer, uint32_t size) { 279 | for (uint32_t idx = 0; idx < size; ++idx) { 280 | if (buffer[idx] != 0xff) return true; 281 | } 282 | return false; 283 | } 284 | 285 | bool ProgramFlash(const char *input_file_name, uint32_t chip_size) { 286 | uint32_t prog_size; 287 | uint8_t* prog = ReadFile(input_file_name, &prog_size); 288 | if (NULL == prog) { 289 | return false; 290 | } 291 | printf("Erasing...");fflush(stdout); 292 | SPICommonCommand(E_CC_WRITE_AFTER_EWSR, 1, 0, 1, 0); // Unprotect the Status Register 293 | SPICommonCommand(E_CC_WRITE_AFTER_WREN, 1, 0, 1, 0); // Unprotect the flash 294 | SPICommonCommand(E_CC_ERASE, 0xc7, 0, 0, 0); // Chip Erase 295 | printf("done\n"); 296 | 297 | //RTD266x can program only 256 bytes at a time. 298 | uint8_t buffer[256]; 299 | uint8_t b; 300 | uint32_t addr = 0; 301 | uint8_t* data_ptr = prog; 302 | uint32_t data_len = prog_size; 303 | InitCRC(); 304 | do 305 | { 306 | // Wait for programming cycle to finish 307 | do { 308 | b = ReadReg(0x6f); 309 | } while (b & 0x40); 310 | 311 | printf("Writing addr %x\r", addr); 312 | // Fill with 0xff in case we read a partial buffer. 313 | memset(buffer, 0xff, sizeof(buffer)); 314 | uint32_t len = sizeof(buffer); 315 | if (len > data_len) { 316 | len = data_len; 317 | } 318 | memcpy(buffer, data_ptr, len); 319 | data_ptr += len; 320 | data_len -= len; 321 | 322 | if (ShouldProgramPage(buffer, sizeof(buffer))) { 323 | // Set program size-1 324 | WriteReg(0x71, 255); 325 | 326 | // Set the programming address 327 | WriteReg(0x64, addr >> 16); 328 | WriteReg(0x65, addr >> 8); 329 | WriteReg(0x66, addr); 330 | 331 | // Write the content to register 0x70 332 | // Out USB gizmo supports max 63 bytes at a time. 333 | WriteBytesToAddr(0x70, buffer, 32); 334 | WriteBytesToAddr(0x70, buffer + 32, 32); 335 | WriteBytesToAddr(0x70, buffer + 64, 32); 336 | WriteBytesToAddr(0x70, buffer + 96, 32); 337 | WriteBytesToAddr(0x70, buffer + 128, 32); 338 | WriteBytesToAddr(0x70, buffer + 160, 32); 339 | WriteBytesToAddr(0x70, buffer + 192, 32); 340 | WriteBytesToAddr(0x70, buffer + 224, 32); 341 | 342 | WriteReg(0x6f, 0xa0); // Start Programing 343 | } 344 | ProcessCRC(buffer, sizeof(buffer)); 345 | addr += 256; 346 | } while (addr < chip_size && data_len != 0); 347 | delete [] prog; 348 | 349 | // Wait for programming cycle to finish 350 | do { 351 | b = ReadReg(0x6f); 352 | } while (b & 0x40); 353 | 354 | SPICommonCommand(E_CC_WRITE_AFTER_EWSR, 1, 0, 1, 0x1c); // Unprotect the Status Register 355 | SPICommonCommand(E_CC_WRITE_AFTER_WREN, 1, 0, 1, 0x1c); // Protect the flash 356 | 357 | uint8_t data_crc = GetCRC(); 358 | uint8_t chip_crc = SPIComputeCRC(0, addr - 1); 359 | printf("Received data CRC %02x\n", data_crc); 360 | printf("Chip CRC %02x\n", chip_crc); 361 | return data_crc == chip_crc; 362 | } 363 | 364 | #define VERSION "0.1" 365 | #define BACKUP 1 366 | #define FLASH 2 367 | 368 | static char help[] = 369 | { 370 | "RTD266X programming tool " VERSION " \n\n" 371 | "Tool to program an RTD266X device using an I2C port in linux (including direct\n" 372 | "flashing using a VGA cable and a VGA graphics card)\n" 373 | "Remember to modprobe i2c-dev!\n" 374 | "Usage: rtd_prog -d [-option[s]]\n" 375 | "Options:\n" 376 | " -d : Specify the i2c bus to use, e.g. -d 1 means /dev/i2c-1\n" 377 | " Given alone this option will just print some info about\n" 378 | " the board (if it finds one)\n" 379 | " -b : Backup the current firmware on the RTD266X to a file\n" 380 | " -f : Flash a file to the RTD266X\n" 381 | }; 382 | 383 | int main(int argc, char** argv) 384 | { 385 | int deviceno = -1; 386 | char *filename = NULL; 387 | int action = -1; 388 | argv++; 389 | 390 | if (argc == 1) { 391 | printf("%s", help); 392 | exit(EXIT_FAILURE); 393 | } 394 | 395 | /* Get arguments */ 396 | while (--argc > 0) { 397 | if (strcmp(*argv, "-d") == 0) { 398 | if (--argc) { 399 | deviceno = atol(*(++argv)); 400 | } 401 | else { 402 | fprintf(stderr, "Error: No argument given for -d\n"); 403 | printf("%s", help); 404 | exit(EXIT_FAILURE); 405 | } 406 | } 407 | else if (strcmp(*argv, "-b") == 0) { 408 | if (--argc) { 409 | filename = *(++argv); 410 | action = BACKUP; 411 | } 412 | else { 413 | fprintf(stderr, "Error: No argument given for -b\n"); 414 | printf("%s", help); 415 | exit(EXIT_FAILURE); 416 | } 417 | } 418 | else if (strcmp(*argv, "-f") == 0) { 419 | if (--argc) { 420 | filename = *(++argv); 421 | action = FLASH; 422 | } 423 | else { 424 | fprintf(stderr, "Error: No argument given for -f\n"); 425 | printf("%s", help); 426 | exit(EXIT_FAILURE); 427 | } 428 | } 429 | else if (strcmp(*argv, "-h") == 0) { 430 | printf("%s", help); 431 | exit(EXIT_SUCCESS); 432 | } 433 | else { 434 | fprintf(stderr, "Error: unknown option \"%s\"\n", *argv); 435 | printf("%s", help); 436 | exit(EXIT_FAILURE); 437 | } 438 | argv++; 439 | } 440 | 441 | /* (A != B) is equivalent to XOR for booleans */ 442 | if (deviceno == -1 || ((filename == NULL) != (action == -1))) { 443 | fprintf(stderr, "Error: not enough options\n"); 444 | printf("%s", help); 445 | } 446 | 447 | uint8_t b; 448 | if (!InitI2C(deviceno)) { 449 | //printf("Can't connect to the USB device. Check the cable.\n"); 450 | //return -1; 451 | exit(EXIT_FAILURE); 452 | } 453 | printf("I2C ready\n"); 454 | SetI2CAddr(0x4a); 455 | 456 | printf("Attempting to read chip ID... "); 457 | const FlashDesc* chip; 458 | bool cnt = false; 459 | int i = 0; 460 | char rotate[] = "|\\-/"; 461 | do { 462 | i++; 463 | if (!WriteReg(0x6f, 0x80)) { // Enter ISP mode 464 | if (cnt == false) printf("Write to 6F failed, keep trying... "); 465 | //return -2; 466 | cnt = true; 467 | printf("\b%c", rotate[(i % 4)]); 468 | fflush(stdout); 469 | continue; 470 | } 471 | else { 472 | cnt = false; 473 | printf("\n"); 474 | } 475 | b = ReadReg(0x6f); 476 | if (!(b & 0x80)) { 477 | if (cnt == false) printf("Can't enable ISP mode, keep trying... "); 478 | //return -3; 479 | cnt = true; 480 | printf("\b%c", rotate[(i % 4)]); 481 | fflush(stdout); 482 | continue; 483 | } 484 | else { 485 | cnt = false; 486 | printf("\n"); 487 | } 488 | uint32_t jedec_id = SPICommonCommand(E_CC_READ, 0x9f, 3, 0, 0); 489 | printf("\nJEDEC ID: 0x%02x\n", jedec_id); 490 | chip = FindChip(jedec_id); 491 | if (NULL == chip) { 492 | printf("Unknown chip ID\n"); 493 | CloseI2C(); 494 | exit(EXIT_FAILURE); 495 | } 496 | } while(cnt); 497 | printf("Manufacturer "); 498 | PrintManufacturer(GetManufacturerId(chip->jedec_id)); 499 | printf("\n"); 500 | printf("Chip: %s\n", chip->device_name); 501 | printf("Size: %dKB\n", chip->size_kb); 502 | 503 | // Setup flash command codes 504 | SetupChipCommands(chip->jedec_id); 505 | 506 | b = SPICommonCommand(E_CC_READ, 0x5, 1, 0, 0); 507 | printf("Flash status register: 0x%02x\n", b); 508 | 509 | //#if 1 510 | if (action == BACKUP) 511 | SaveFlash(filename, chip->size_kb * 1024); 512 | else if (action == FLASH) 513 | ProgramFlash(filename, chip->size_kb * 1024); 514 | // SaveFlash("flash-test.bin", chip->size_kb * 1024); 515 | //#else 516 | // ProgramFlash("1024x600.bin", chip->size_kb * 1024); 517 | //#endif 518 | CloseI2C(); 519 | return 0; 520 | } 521 | --------------------------------------------------------------------------------