summaryrefslogtreecommitdiff
path: root/tools/proxyclient/experiments/jpeg.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/proxyclient/experiments/jpeg.py')
-rw-r--r--tools/proxyclient/experiments/jpeg.py1222
1 files changed, 1222 insertions, 0 deletions
diff --git a/tools/proxyclient/experiments/jpeg.py b/tools/proxyclient/experiments/jpeg.py
new file mode 100644
index 0000000..1e53a6b
--- /dev/null
+++ b/tools/proxyclient/experiments/jpeg.py
@@ -0,0 +1,1222 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+from m1n1.setup import *
+from m1n1.hw.dart import DART, DARTRegs
+from m1n1.hw.jpeg import *
+from m1n1.utils import *
+import argparse
+import struct
+import time
+from enum import IntEnum
+from PIL import Image, ImageDraw
+
+
+def divroundup(val, div):
+ return (val + div - 1) // div
+
+
+def yuv2rgb(y, u, v):
+ y -= 16
+ u -= 128
+ v -= 128
+
+ y /= 255
+ u /= 255
+ v /= 255
+
+ r = y + 1.13983 * v
+ g = y - 0.39465 * u - 0.58060 * v
+ b = y + 2.03211 * u
+
+ r = min(255, max(0, int(r * 255)))
+ g = min(255, max(0, int(g * 255)))
+ b = min(255, max(0, int(b * 255)))
+
+ return (r, g, b)
+
+
+def rgb2yuv(r, g, b):
+ r /= 255
+ g /= 255
+ b /= 255
+
+ y = 0.299*r + 0.587*g + 0.114*b
+ u = -0.14713*r - 0.28886*g + 0.436*b
+ v = 0.615*r - 0.51499*g - 0.10001*b
+
+ y = y * 255 + 16
+ u = u * 255 + 128
+ v = v * 255 + 128
+
+ y = min(255, max(0, int(y)))
+ u = min(255, max(0, int(u)))
+ v = min(255, max(0, int(v)))
+
+ return (y, u, v)
+
+
+ap = argparse.ArgumentParser(description='JPEG block experiment')
+ap.add_argument("--jpeg", dest='which_jpeg', type=str, default='jpeg0',
+ help='which JPEG instance (jpeg0/jpeg1)')
+g = ap.add_mutually_exclusive_group(required=True)
+g.add_argument("-e", "--encode", action='store_true')
+g.add_argument("-d", "--decode", action='store_true')
+ap.add_argument("--raw-output", type=str, required=False)
+ap.add_argument("--decode-scale", type=int, required=False, default=1)
+ap.add_argument("--decode-pixelfmt", type=str, required=False, default='RGBA')
+ap.add_argument("--decode-rgba-alpha", type=int, required=False, default=255)
+ap.add_argument("--encode-subsampling", type=str, required=False, default='444')
+ap.add_argument("--encode-rst-interval", type=int, required=False)
+ap.add_argument("--encode-pixelfmt", type=str, required=False, default='RGB888')
+ap.add_argument("input", type=str)
+ap.add_argument("output", type=str)
+args = ap.parse_args()
+
+# print(args)
+
+# Perform necessary pre-parsing
+if args.decode:
+ assert args.decode_scale in [1, 2, 4, 8]
+ decode_scale = args.decode_scale
+ # FIXME: verify behavior on non-evenly-divisible sizes
+
+ assert args.decode_pixelfmt in [
+ 'RGBA',
+ 'BGRA',
+ 'RGB565',
+ 'YUV422-CbYCrY',
+ 'YUV422-YCbYCr',
+ 'YUV422-planar',
+ 'YUV420-planar',
+ 'YUV444-planar',
+ ]
+ pixfmt = args.decode_pixelfmt
+
+ with open(args.input, 'rb') as f:
+ jpeg_data = f.read()
+
+ found_sof0 = False
+
+ jpeg_work = jpeg_data
+ while jpeg_work:
+ seg_marker = struct.unpack(">H", jpeg_work[:2])[0]
+ print(f"Seg {seg_marker:04X}")
+ if seg_marker == 0xFFD8:
+ # SOI
+ jpeg_work = jpeg_work[2:]
+ elif seg_marker == 0xFFDA:
+ # SOS
+ break
+ else:
+ seg_len = struct.unpack(">H", jpeg_work[2:4])[0]
+ assert seg_len >= 2
+ seg_data = jpeg_work[4:4 + seg_len - 2]
+ jpeg_work = jpeg_work[4 + seg_len - 2:]
+
+ if seg_marker == 0xFFC0:
+ # SOF0
+ assert not found_sof0
+ found_sof0 = True
+ sof0 = struct.unpack(">BHHB", seg_data[:6])
+ (jpeg_bpp, jpeg_H, jpeg_W, jpeg_components_cnt) = sof0
+ # it is not yet verified what the requirements are for inputs
+ assert jpeg_bpp == 8
+ assert jpeg_components_cnt == 1 or jpeg_components_cnt == 3
+ if jpeg_components_cnt == 1:
+ jpeg_MODE = '400'
+ else:
+ jpeg_components = {}
+ for i in range(jpeg_components_cnt):
+ comp_id, comp_sampling, _ = seg_data[6+3*i:6+3*(i+1)]
+ jpeg_components[comp_id] = comp_sampling
+ assert 1 in jpeg_components
+ comp_Y = jpeg_components[1]
+ assert 2 in jpeg_components
+ comp_Cb = jpeg_components[2]
+ assert 3 in jpeg_components
+ comp_Cr = jpeg_components[3]
+
+ if (comp_Y, comp_Cb, comp_Cr) == (0x11, 0x11, 0x11):
+ jpeg_MODE = '444'
+ elif (comp_Y, comp_Cb, comp_Cr) == (0x21, 0x11, 0x11):
+ jpeg_MODE = '422'
+ elif (comp_Y, comp_Cb, comp_Cr) == (0x22, 0x11, 0x11):
+ jpeg_MODE = '420'
+ elif (comp_Y, comp_Cb, comp_Cr) == (0x41, 0x11, 0x11):
+ jpeg_MODE = '411'
+ else:
+ # TODO: 422-vertical, others???
+ # Is it possible to implement them?
+ print("Unsupported subsampling mode")
+ assert False
+
+ assert found_sof0
+ print(f"JPEG is {jpeg_W}x{jpeg_H} with subsampling {jpeg_MODE}")
+
+ if jpeg_MODE == '444' or jpeg_MODE == '400':
+ macroblock_W, macroblock_H = 8, 8
+ elif jpeg_MODE == '422':
+ macroblock_W, macroblock_H = 16, 8
+ elif jpeg_MODE == '420':
+ macroblock_W, macroblock_H = 16, 16
+ elif jpeg_MODE == '411':
+ macroblock_W, macroblock_H = 32, 8
+ else:
+ assert False
+
+ # FIXME: Exactly how much extra memory do we need to allocate?
+ surface_W = divroundup(jpeg_W // decode_scale, macroblock_W) * macroblock_W
+ surface_H = divroundup(jpeg_H // decode_scale, macroblock_H) * macroblock_H
+ if pixfmt in ['RGBA', 'BGRA']:
+ BYTESPP = 4
+ elif pixfmt in ['RGB565', 'YUV422-CbYCrY', 'YUV422-YCbYCr']:
+ BYTESPP = 2
+ elif pixfmt in ['YUV422-planar', 'YUV420-planar', 'YUV444-planar']:
+ BYTESPP = 1
+ else:
+ assert False
+ surface_stride = surface_W * BYTESPP
+ surface_sz = surface_stride*surface_H
+
+ if pixfmt == 'YUV422-planar':
+ P1_MULW = 1 # FIXME UGLY
+ P1_DIVW = 1
+ P1_DIVH = 1
+ elif pixfmt == 'YUV420-planar':
+ P1_MULW = 1
+ P1_DIVW = 1
+ P1_DIVH = 2
+ elif pixfmt == 'YUV444-planar':
+ P1_MULW = 2
+ P1_DIVW = 1
+ P1_DIVH = 1
+ if pixfmt in ['YUV422-planar', 'YUV420-planar', 'YUV444-planar']:
+ surface_P1_W = surface_W * P1_MULW // P1_DIVW
+ surface_P1_H = surface_H // P1_DIVH
+ surface_P1_stride = surface_P1_W
+ surface_P1_off = surface_sz
+ surface_sz += surface_P1_stride*surface_P1_H
+ else:
+ surface_P1_stride = 0
+ surface_P1_off = 0
+
+ input_mem_sz = align_up(len(jpeg_data))
+ print(f"Using size {input_mem_sz:08X} for JPEG data")
+
+ output_mem_sz = align_up(surface_sz)
+ print(f"Using size {output_mem_sz:08X} for output image")
+else:
+ assert args.encode_subsampling in ['444', '422', '420', '400']
+ if args.encode_subsampling == '444' or args.encode_subsampling == '400':
+ macroblock_W, macroblock_H = 8, 8
+ elif args.encode_subsampling == '422':
+ macroblock_W, macroblock_H = 16, 8
+ elif args.encode_subsampling == '420':
+ macroblock_W, macroblock_H = 16, 16
+ else:
+ assert False
+
+ assert args.encode_pixelfmt in [
+ 'RGB888',
+ 'RGB101010',
+ 'RGB565',
+ 'YUV10',
+ 'YUV-linear',
+ 'YUV444-planar',
+ 'YUV422-planar',
+ 'YUV420-planar',
+ ]
+ pixfmt = args.encode_pixelfmt
+
+ # Driver doesn't support this either
+ if pixfmt == 'YUV-linear' and args.encode_subsampling == '444':
+ print("WARNING: This combination does not appear to work!!!")
+ if pixfmt == 'YUV422-planar' and args.encode_subsampling == '444':
+ print("WARNING: This combination does not appear to work!!!")
+ if pixfmt == 'YUV420-planar' and args.encode_subsampling == '444':
+ print("WARNING: This combination does not appear to work!!!")
+
+ image_data = b''
+ image_data_P1 = b''
+ with Image.open(args.input) as im:
+ im_W, im_H = im.size
+
+ if pixfmt != 'YUV420-planar':
+ for y in range(im_H):
+ for x in range(im_W):
+ r, g, b = im.getpixel((x, y))
+ if pixfmt == 'RGB888':
+ image_data += struct.pack("BBBB", r, g, b, 255)
+ elif pixfmt == 'RGB101010':
+ image_data += struct.pack("<I", (r << 2) | (g << 12) | (b << 22))
+ elif pixfmt == 'RGB565':
+ image_data += struct.pack("<H", (r >> 3) | ((g >> 2) << 5) | ((b >> 3) << 11))
+ elif pixfmt == 'YUV10':
+ # absolute garbage color space conversion
+ # for demonstration purposes only
+ y_, u_, v_ = rgb2yuv(r, g, b)
+ image_data += struct.pack("<I", (y_ << 2) | (u_ << 12) | (v_ << 22))
+ elif pixfmt == 'YUV-linear':
+ # garbage color space conversion, garbage subsampling
+ # for demonstration purposes only
+ y_, u_, v_ = rgb2yuv(r, g, b)
+ if x & 1 == 0:
+ color = u_
+ else:
+ color = v_
+ image_data += struct.pack("BB", y_, color)
+ elif pixfmt == 'YUV444-planar':
+ # garbage color space conversion
+ # for demonstration purposes only
+ y_, u_, v_ = rgb2yuv(r, g, b)
+ image_data += struct.pack("B", y_)
+ image_data_P1 += struct.pack("BB", u_, v_)
+ elif pixfmt == 'YUV422-planar':
+ # garbage color space conversion, garbage subsampling
+ # for demonstration purposes only
+ y_, u_, v_ = rgb2yuv(r, g, b)
+ if x & 1 == 0:
+ color = u_
+ else:
+ color = v_
+ image_data += struct.pack("B", y_)
+ image_data_P1 += struct.pack("B", color)
+ else:
+ assert False
+ else:
+ for y in range(im_H):
+ for x in range(im_W):
+ r, g, b = im.getpixel((x, y))
+ # garbage color space conversion, garbage subsampling
+ # for demonstration purposes only
+ y_, u_, v_ = rgb2yuv(r, g, b)
+ if x & 1 == 0:
+ color = u_
+ else:
+ color = v_
+ image_data += struct.pack("B", y_)
+ if y & 1 == 0:
+ image_data_P1 += struct.pack("B", color)
+
+ if pixfmt in ['RGB888', 'RGB101010', 'YUV10']:
+ BYTESPP = 4
+ BYTESPP_P1 = 0
+ P1_DIVH = 1
+ elif pixfmt in ['RGB565', 'YUV-linear']:
+ BYTESPP = 2
+ BYTESPP_P1 = 0
+ P1_DIVH = 1
+ elif pixfmt == 'YUV444-planar':
+ BYTESPP = 1
+ BYTESPP_P1 = 2
+ P1_DIVH = 1
+ elif pixfmt == 'YUV422-planar':
+ BYTESPP = 1
+ BYTESPP_P1 = 1
+ P1_DIVH = 1
+ elif pixfmt == 'YUV420-planar':
+ BYTESPP = 1
+ BYTESPP_P1 = 1
+ P1_DIVH = 2
+ else:
+ assert False
+ surface_stride = im_W * BYTESPP
+ surface_sz = surface_stride * im_H
+ surface_P1_off = surface_sz
+ print(f"Plane 1 offset at {surface_P1_off:08X}")
+ surface_P1_stride = im_W * BYTESPP_P1
+ surface_sz += surface_P1_stride * im_H // P1_DIVH
+ input_mem_sz = align_up(surface_sz)
+
+ output_mem_sz = input_mem_sz
+
+ print(f"Using size {input_mem_sz:08X} for input image")
+ print(f"Using size {output_mem_sz:08X} for output data")
+
+# Turn on the JPEG block
+p.pmgr_adt_clocks_enable(f'/arm-io/dart-{args.which_jpeg}')
+p.pmgr_adt_clocks_enable(f'/arm-io/{args.which_jpeg}')
+
+dart = DART.from_adt(u, f'/arm-io/dart-{args.which_jpeg}')
+dart.initialize()
+
+jpeg_base, _ = u.adt[f'/arm-io/{args.which_jpeg}'].get_reg(0)
+jpeg = JPEGRegs(u, jpeg_base)
+
+
+def reset_block():
+ jpeg.MODE.val = 0x100
+ jpeg.MODE.val = 0x13e
+
+ set_default_regs()
+
+ jpeg.MODE.val = 0x17f
+ for _ in range(10000):
+ v = jpeg.REG_0x1004.val
+ if v == 0:
+ break
+ print(f"reset 1 -- {v}")
+ if (v := jpeg.REG_0x1004.val) != 0:
+ print(f"reset 1 failed! -- {v}")
+ assert False
+
+ jpeg.RST_INTERVAL.val = 1
+ for _ in range(2500):
+ v = jpeg.RST_INTERVAL.val
+ if v == 1:
+ break
+ print(f"reset 2 -- {v}")
+ if (v := jpeg.RST_INTERVAL.val) != 1:
+ print(f"reset 2 failed! -- {v}")
+ assert False
+ jpeg.RST_INTERVAL.val = 0
+
+ jpeg.ENABLE_RST_LOGGING.val = 0
+ jpeg.REG_0x1a8.val = 0
+ jpeg.REG_0x1ac.val = 0
+ jpeg.REG_0x1b0.val = 0
+ jpeg.REG_0x1b4.val = 0
+ jpeg.REG_0x1bc.val = 0
+ jpeg.REG_0x1c0.val = 0
+ jpeg.REG_0x1c4.val = 0
+ jpeg.REG_0x1c8.val = 0
+ jpeg.REG_0x1cc.val = 0
+ jpeg.REG_0x1d0.val = 0
+ jpeg.REG_0x1d4.val = 0
+
+ jpeg.MODE.val = 0x143
+
+
+def set_default_regs(param1=0):
+ jpeg.REG_0x0.val = 0
+ jpeg.REG_0x0.val = 0
+ jpeg.REG_0x4.val = 0
+ jpeg.CODEC.val = 0
+ jpeg.REG_0x2c.val = 0
+ jpeg.REG_0x30.val = 0
+ jpeg.REG_0x34.val = 1
+ jpeg.REG_0x38.val = 1
+ jpeg.CHROMA_HALVE_H_TYPE1.val = 0
+ jpeg.CHROMA_HALVE_H_TYPE2.val = 0
+ jpeg.CHROMA_HALVE_V_TYPE1.val = 0
+ jpeg.CHROMA_HALVE_V_TYPE2.val = 0
+ jpeg.CHROMA_DOUBLE_H.val = 0
+ jpeg.CHROMA_QUADRUPLE_H.val = 0
+ jpeg.CHROMA_DOUBLE_V.val = 0
+ jpeg.PLANAR_CHROMA_HALVING.val = 0
+ jpeg.PX_USE_PLANE1.val = 0
+ jpeg.PX_TILES_W.val = 1
+ jpeg.PX_TILES_H.val = 1
+ jpeg.PX_PLANE0_WIDTH.val = 1
+ jpeg.PX_PLANE0_HEIGHT.val = 1
+ jpeg.PX_PLANE0_TILING_H.val = 1
+ jpeg.PX_PLANE0_TILING_V.val = 1
+ jpeg.PX_PLANE0_STRIDE.val = 1
+ jpeg.PX_PLANE1_WIDTH.val = 1
+ jpeg.PX_PLANE1_HEIGHT.val = 1
+ jpeg.PX_PLANE1_TILING_H.val = 1
+ jpeg.PX_PLANE1_TILING_V.val = 1
+ jpeg.PX_PLANE1_STRIDE.val = 1
+ jpeg.INPUT_START1.val = 0
+ jpeg.INPUT_START2.val = 0
+ jpeg.REG_0x94.val = 1
+ jpeg.REG_0x98.val = 1
+ jpeg.INPUT_END.val = 0xffffffff
+ jpeg.OUTPUT_START1.val = 0
+ jpeg.OUTPUT_START2.val = 0
+ jpeg.OUTPUT_END.val = 0xffffffff
+ for i in range(11):
+ jpeg.MATRIX_MULT[i].val = 0
+ for i in range(10):
+ jpeg.DITHER[i].val = 0xff
+ jpeg.ENCODE_PIXEL_FORMAT.val = 0
+ jpeg.ENCODE_COMPONENT0_POS.val = 0
+ jpeg.ENCODE_COMPONENT1_POS.val = 0
+ jpeg.ENCODE_COMPONENT2_POS.val = 0
+ jpeg.ENCODE_COMPONENT3_POS.val = 0
+ jpeg.CONVERT_COLOR_SPACE.val = 0
+ jpeg.REG_0x118.val = 0
+ jpeg.REG_0x11c.val = 0
+ jpeg.REG_0x120.val = 0
+ jpeg.TILING_ENABLE.val = 0
+ jpeg.TILING_PLANE0.val = 0
+ jpeg.TILING_PLANE1.val = 0
+ jpeg.DECODE_MACROBLOCKS_W.val = 0
+ jpeg.DECODE_MACROBLOCKS_H.val = 0
+ jpeg.SCALE_FACTOR.val = 0
+ jpeg.DECODE_PIXEL_FORMAT.val = 0
+ jpeg.YUV422_ORDER.val = 0
+ jpeg.RGBA_ORDER.val = 0
+ jpeg.RGBA_ALPHA.val = 0
+ jpeg.RIGHT_EDGE_PIXELS.val = 0
+ jpeg.BOTTOM_EDGE_PIXELS.val = 0
+ jpeg.RIGHT_EDGE_SAMPLES.val = 0
+ jpeg.BOTTOM_EDGE_SAMPLES.val = 0
+
+ # this is always done on the m1 max hwrev
+ jpeg.REG_0x1fc.val = 0
+ jpeg.REG_0x200.val = 0
+ jpeg.REG_0x204.val = 0
+ jpeg.REG_0x208.val = 0
+ jpeg.REG_0x214.val = 0
+ jpeg.REG_0x218.val = 0
+ jpeg.REG_0x21c.val = 0
+ jpeg.REG_0x220.val = 0
+ jpeg.REG_0x224.val = 0
+ jpeg.REG_0x228.val = 0
+ jpeg.REG_0x22c.val = 0
+ jpeg.REG_0x230.val = 0
+ jpeg.REG_0x234.val = 0x1f40
+ jpeg.REG_0x244.val = 0
+ jpeg.REG_0x248.val = 0
+ jpeg.REG_0x258.val = 0
+ jpeg.REG_0x25c.val = 0
+ jpeg.REG_0x23c.val = 0
+ jpeg.REG_0x240.val = 0
+ jpeg.REG_0x250.val = 0
+ jpeg.REG_0x254.val = 0
+
+ jpeg.REG_0x160.val = param1
+ jpeg.TIMEOUT.val = 0
+ jpeg.REG_0x20.val = 0xff
+
+
+print(f"HW revision is {jpeg.HWREV}")
+reset_block()
+
+input_buf_phys = u.heap.memalign(0x4000, input_mem_sz)
+output_buf_phys = u.heap.memalign(0x4000, output_mem_sz)
+print(f"buffers (phys) {input_buf_phys:016X} {output_buf_phys:016X}")
+
+input_buf_iova = dart.iomap(0, input_buf_phys, input_mem_sz)
+output_buf_iova = dart.iomap(0, output_buf_phys, output_mem_sz)
+print(f"buffers (iova) {input_buf_iova:08X} {output_buf_iova:08X}")
+# dart.dump_all()
+
+iface.writemem(input_buf_phys, b'\xAA' * input_mem_sz)
+iface.writemem(output_buf_phys, b'\xAA' * output_mem_sz)
+
+
+if args.decode:
+ iface.writemem(input_buf_phys, jpeg_data)
+ print("JPEG uploaded")
+
+ jpeg.REG_0x34 = 1
+ jpeg.REG_0x2c = 0
+ jpeg.REG_0x38 = 0
+ if jpeg_MODE == '444':
+ jpeg.CODEC.set(CODEC=E_CODEC._444)
+ elif jpeg_MODE == '400':
+ jpeg.CODEC.set(CODEC=E_CODEC._400)
+ elif jpeg_MODE == '422':
+ jpeg.CODEC.set(CODEC=E_CODEC._422)
+ elif jpeg_MODE == '420':
+ jpeg.CODEC.set(CODEC=E_CODEC._420)
+ elif jpeg_MODE == '411':
+ jpeg.CODEC.set(CODEC=E_CODEC._411)
+ else:
+ assert False
+ if pixfmt == 'RGBA' or pixfmt == 'BGRA':
+ jpeg.DECODE_PIXEL_FORMAT.set(FORMAT=E_DECODE_PIXEL_FORMAT.RGBA8888)
+ elif pixfmt == 'RGB565':
+ jpeg.DECODE_PIXEL_FORMAT.set(FORMAT=E_DECODE_PIXEL_FORMAT.RGB565)
+ elif pixfmt == 'YUV422-CbYCrY' or pixfmt == 'YUV422-YCbYCr':
+ jpeg.DECODE_PIXEL_FORMAT.set(FORMAT=E_DECODE_PIXEL_FORMAT.YUV422_linear)
+ elif pixfmt == 'YUV422-planar':
+ jpeg.DECODE_PIXEL_FORMAT.set(FORMAT=E_DECODE_PIXEL_FORMAT.YUV422_planar)
+ elif pixfmt == 'YUV420-planar':
+ jpeg.DECODE_PIXEL_FORMAT.set(FORMAT=E_DECODE_PIXEL_FORMAT.YUV420_planar)
+ elif pixfmt == 'YUV444-planar':
+ jpeg.DECODE_PIXEL_FORMAT.set(FORMAT=E_DECODE_PIXEL_FORMAT.YUV444_planar)
+ else:
+ assert False
+
+ if pixfmt in ['YUV422-planar', 'YUV420-planar', 'YUV444-planar']:
+ jpeg.PX_USE_PLANE1 = 1
+ jpeg.PX_PLANE1_WIDTH = jpeg_W * P1_MULW // P1_DIVW // decode_scale - 1
+ jpeg.PX_PLANE1_HEIGHT = jpeg_H // P1_DIVH // decode_scale - 1
+ else:
+ jpeg.PX_USE_PLANE1 = 0
+ jpeg.PX_PLANE0_WIDTH = jpeg_W*BYTESPP // decode_scale - 1
+ jpeg.PX_PLANE0_HEIGHT = jpeg_H // decode_scale - 1
+ jpeg.TIMEOUT = 266000000
+
+ jpeg.REG_0x94 = 0x1f
+ jpeg.REG_0x98 = 1
+
+ jpeg.DECODE_MACROBLOCKS_W = divroundup(jpeg_W, macroblock_W)
+ jpeg.DECODE_MACROBLOCKS_H = divroundup(jpeg_H, macroblock_H)
+ right_edge_px = \
+ jpeg_W - divroundup(jpeg_W, macroblock_W)*macroblock_W + macroblock_W
+ bot_edge_px = \
+ jpeg_H - divroundup(jpeg_H, macroblock_H)*macroblock_H + macroblock_H
+ # XXX changing this does not seem to do anything.
+ # Does it possibly affect scaling down?
+ jpeg.RIGHT_EDGE_PIXELS.val = right_edge_px
+ jpeg.BOTTOM_EDGE_PIXELS.val = bot_edge_px
+ jpeg.RIGHT_EDGE_SAMPLES.val = right_edge_px // (macroblock_W // 8)
+ jpeg.BOTTOM_EDGE_SAMPLES.val = bot_edge_px // (macroblock_H // 8)
+
+ jpeg.PX_TILES_H = divroundup(jpeg_H, macroblock_H)
+ # FIXME explain this
+ if pixfmt in ['RGBA', 'BGRA', 'RGB565', 'YUV444-planar']:
+ jpeg.PX_TILES_W = divroundup(jpeg_W // decode_scale, macroblock_W)
+ else:
+ jpeg.PX_TILES_W = divroundup(jpeg_W // decode_scale, max(macroblock_W, 16))
+ if pixfmt == 'RGBA' or pixfmt == 'BGRA':
+ if jpeg_MODE == '444' or jpeg_MODE == '400':
+ jpeg.PX_PLANE0_TILING_H = 4
+ jpeg.PX_PLANE0_TILING_V = 8 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 1
+ jpeg.PX_PLANE1_TILING_V = 1
+ elif jpeg_MODE == '422':
+ jpeg.PX_PLANE0_TILING_H = 8
+ jpeg.PX_PLANE0_TILING_V = 8 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 1
+ jpeg.PX_PLANE1_TILING_V = 1
+ elif jpeg_MODE == '420':
+ jpeg.PX_PLANE0_TILING_H = 8
+ jpeg.PX_PLANE0_TILING_V = 16 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 0
+ jpeg.PX_PLANE1_TILING_V = 0
+ elif jpeg_MODE == '411':
+ jpeg.PX_PLANE0_TILING_H = 16
+ jpeg.PX_PLANE0_TILING_V = 8 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 0
+ jpeg.PX_PLANE1_TILING_V = 0
+ else:
+ assert False
+ elif pixfmt == 'RGB565':
+ if jpeg_MODE == '444' or jpeg_MODE == '400':
+ jpeg.PX_PLANE0_TILING_H = 2
+ jpeg.PX_PLANE0_TILING_V = 8 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 1
+ jpeg.PX_PLANE1_TILING_V = 1
+ elif jpeg_MODE == '422':
+ jpeg.PX_PLANE0_TILING_H = 4
+ jpeg.PX_PLANE0_TILING_V = 8 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 1
+ jpeg.PX_PLANE1_TILING_V = 1
+ elif jpeg_MODE == '420':
+ jpeg.PX_PLANE0_TILING_H = 4
+ jpeg.PX_PLANE0_TILING_V = 16 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 0
+ jpeg.PX_PLANE1_TILING_V = 0
+ elif jpeg_MODE == '411':
+ jpeg.PX_PLANE0_TILING_H = 8
+ jpeg.PX_PLANE0_TILING_V = 8 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 0
+ jpeg.PX_PLANE1_TILING_V = 0
+ else:
+ assert False
+ elif pixfmt == 'YUV422-CbYCrY' or pixfmt == 'YUV422-YCbYCr':
+ if jpeg_MODE == '444' or jpeg_MODE == '400':
+ jpeg.PX_PLANE0_TILING_H = 4
+ jpeg.PX_PLANE0_TILING_V = 8 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 1
+ jpeg.PX_PLANE1_TILING_V = 1
+ elif jpeg_MODE == '422':
+ jpeg.PX_PLANE0_TILING_H = 4
+ jpeg.PX_PLANE0_TILING_V = 8 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 1
+ jpeg.PX_PLANE1_TILING_V = 1
+ elif jpeg_MODE == '420':
+ jpeg.PX_PLANE0_TILING_H = 4
+ jpeg.PX_PLANE0_TILING_V = 16 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 0
+ jpeg.PX_PLANE1_TILING_V = 0
+ elif jpeg_MODE == '411':
+ jpeg.PX_PLANE0_TILING_H = 8
+ jpeg.PX_PLANE0_TILING_V = 8 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 0
+ jpeg.PX_PLANE1_TILING_V = 0
+ else:
+ assert False
+ elif pixfmt == 'YUV422-planar':
+ if jpeg_MODE == '444' or jpeg_MODE == '400':
+ jpeg.PX_PLANE0_TILING_H = 2
+ jpeg.PX_PLANE0_TILING_V = 8 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 2
+ jpeg.PX_PLANE1_TILING_V = 8 // decode_scale
+ elif jpeg_MODE == '422':
+ jpeg.PX_PLANE0_TILING_H = 2
+ jpeg.PX_PLANE0_TILING_V = 8 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 2
+ jpeg.PX_PLANE1_TILING_V = 8 // decode_scale
+ elif jpeg_MODE == '420':
+ jpeg.PX_PLANE0_TILING_H = 2
+ jpeg.PX_PLANE0_TILING_V = 16 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 2
+ jpeg.PX_PLANE1_TILING_V = 16 // decode_scale
+ elif jpeg_MODE == '411':
+ jpeg.PX_PLANE0_TILING_H = 4
+ jpeg.PX_PLANE0_TILING_V = 8 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 4
+ jpeg.PX_PLANE1_TILING_V = 8 // decode_scale
+ else:
+ assert False
+ elif pixfmt == 'YUV420-planar':
+ if jpeg_MODE == '444' or jpeg_MODE == '400':
+ jpeg.PX_PLANE0_TILING_H = 2
+ jpeg.PX_PLANE0_TILING_V = 8 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 2
+ jpeg.PX_PLANE1_TILING_V = 4 // decode_scale
+ elif jpeg_MODE == '422':
+ jpeg.PX_PLANE0_TILING_H = 2
+ jpeg.PX_PLANE0_TILING_V = 8 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 2
+ jpeg.PX_PLANE1_TILING_V = 4 // decode_scale
+ elif jpeg_MODE == '420':
+ jpeg.PX_PLANE0_TILING_H = 2
+ jpeg.PX_PLANE0_TILING_V = 16 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 2
+ jpeg.PX_PLANE1_TILING_V = 8 // decode_scale
+ elif jpeg_MODE == '411':
+ jpeg.PX_PLANE0_TILING_H = 4
+ jpeg.PX_PLANE0_TILING_V = 8 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 4
+ jpeg.PX_PLANE1_TILING_V = 4 // decode_scale
+ else:
+ assert False
+ elif pixfmt == 'YUV444-planar':
+ if jpeg_MODE == '444' or jpeg_MODE == '400':
+ jpeg.PX_PLANE0_TILING_H = 1
+ jpeg.PX_PLANE0_TILING_V = 8 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 2
+ jpeg.PX_PLANE1_TILING_V = 8 // decode_scale
+ elif jpeg_MODE == '422':
+ # The driver doesn't use this, but guessing seems to be fine?
+ jpeg.PX_PLANE0_TILING_H = 2
+ jpeg.PX_PLANE0_TILING_V = 8 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 4
+ jpeg.PX_PLANE1_TILING_V = 8 // decode_scale
+ elif jpeg_MODE == '420':
+ # The driver doesn't use this, but guessing seems to be fine?
+ jpeg.PX_PLANE0_TILING_H = 2
+ jpeg.PX_PLANE0_TILING_V = 16 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 4
+ jpeg.PX_PLANE1_TILING_V = 16 // decode_scale
+ elif jpeg_MODE == '411':
+ # The driver doesn't use this, but guessing seems to be fine?
+ jpeg.PX_PLANE0_TILING_H = 4
+ jpeg.PX_PLANE0_TILING_V = 8 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 8
+ jpeg.PX_PLANE1_TILING_V = 8 // decode_scale
+ else:
+ assert False
+ else:
+ assert False
+
+ if pixfmt in ['RGBA', 'BGRA', 'RGB565', 'YUV444-planar']:
+ if jpeg_MODE in ['422', '420']:
+ jpeg.CHROMA_DOUBLE_H = 1
+
+ if jpeg_MODE == '411':
+ jpeg.CHROMA_QUADRUPLE_H = 1
+
+ if jpeg_MODE == '420':
+ jpeg.CHROMA_DOUBLE_V = 1
+ elif pixfmt in ["YUV422-CbYCrY", "YUV422-YCbYCr", "YUV422-planar"]:
+ if jpeg_MODE == '444':
+ jpeg.CHROMA_HALVE_H_TYPE1 = 1
+
+ if jpeg_MODE == '411':
+ jpeg.CHROMA_DOUBLE_H = 1
+
+ if jpeg_MODE == '420':
+ jpeg.CHROMA_DOUBLE_V = 1
+ elif pixfmt in ["YUV420-planar"]:
+ if jpeg_MODE == '444':
+ jpeg.CHROMA_HALVE_H_TYPE1 = 1
+
+ if jpeg_MODE in ['444', '422', '411']:
+ jpeg.CHROMA_HALVE_V_TYPE1 = 1
+
+ if jpeg_MODE == '411':
+ jpeg.CHROMA_DOUBLE_H = 1
+ else:
+ assert False
+
+ jpeg.MATRIX_MULT[0].val = 0x100
+ jpeg.MATRIX_MULT[1].val = 0x0
+ jpeg.MATRIX_MULT[2].val = 0x167
+ jpeg.MATRIX_MULT[3].val = 0x100
+ jpeg.MATRIX_MULT[4].val = 0xffffffa8
+ jpeg.MATRIX_MULT[5].val = 0xffffff49
+ jpeg.MATRIX_MULT[6].val = 0x100
+ jpeg.MATRIX_MULT[7].val = 0x1c6
+ jpeg.MATRIX_MULT[8].val = 0x0
+ jpeg.MATRIX_MULT[9].val = 0x0
+ jpeg.MATRIX_MULT[10].val = 0xffffff80
+
+ jpeg.RGBA_ALPHA = args.decode_rgba_alpha
+ jpeg.RGBA_ORDER = pixfmt == "RGBA"
+ jpeg.YUV422_ORDER = pixfmt == "YUV422-YCbYCr"
+
+ if decode_scale == 1:
+ jpeg.SCALE_FACTOR.set(SCALE=E_SCALE.DIV1)
+ elif decode_scale == 2:
+ jpeg.SCALE_FACTOR.set(SCALE=E_SCALE.DIV2)
+ elif decode_scale == 4:
+ jpeg.SCALE_FACTOR.set(SCALE=E_SCALE.DIV4)
+ elif decode_scale == 8:
+ jpeg.SCALE_FACTOR.set(SCALE=E_SCALE.DIV8)
+ else:
+ assert False
+
+ jpeg.INPUT_START1 = input_buf_iova
+ jpeg.INPUT_START2 = 0xdeadbeef
+ jpeg.INPUT_END = input_buf_iova + input_mem_sz
+ jpeg.OUTPUT_START1 = output_buf_iova
+ jpeg.OUTPUT_START2 = output_buf_iova + surface_P1_off
+ jpeg.OUTPUT_END = output_buf_iova + output_mem_sz
+ jpeg.PX_PLANE0_STRIDE = surface_stride
+ jpeg.PX_PLANE1_STRIDE = surface_P1_stride
+
+ jpeg.REG_0x1ac = 0x0
+ jpeg.REG_0x1b0 = 0x0
+ jpeg.REG_0x1b4 = 0x0
+ jpeg.REG_0x1bc = 0x0
+ jpeg.REG_0x1c0 = 0x0
+ jpeg.REG_0x1c4 = 0x0
+
+ jpeg.REG_0x118 = 0x0
+ jpeg.REG_0x11c = 0x1
+
+ jpeg.MODE = 0x177
+ jpeg.REG_0x1028 = 0x400
+
+ jpeg.JPEG_IO_FLAGS = 0x3f
+ jpeg.REG_0x0 = 0x1
+ jpeg.REG_0x1004 = 0x1
+
+ # FIXME: we don't actually know when it's done
+ time.sleep(1)
+
+ print(jpeg.STATUS.reg)
+ print(jpeg.PERFCOUNTER.reg)
+
+ output_data = iface.readmem(output_buf_phys, output_mem_sz)
+ if args.raw_output is not None:
+ with open(args.raw_output, 'wb') as f:
+ f.write(output_data)
+
+ # Just for demonstration purposes, wrangle everything back into RGB
+ with Image.new(
+ mode='RGBA',
+ size=(jpeg_W // decode_scale, jpeg_H // decode_scale)) as im:
+ if pixfmt in ["RGBA", "BGRA", "RGB565"]:
+ for y in range(jpeg_H // decode_scale):
+ for x in range(jpeg_W // decode_scale):
+ block = output_data[
+ y*surface_stride + x*BYTESPP:
+ y*surface_stride + (x+1)*BYTESPP]
+
+ if pixfmt == "RGBA":
+ r, g, b, a = block
+ elif pixfmt == "BGRA":
+ b, g, r, a = block
+ elif pixfmt == "RGB565":
+ rgb = struct.unpack("<H", block)[0]
+ b = (rgb & 0b11111) << 3
+ g = ((rgb >> 5) & 0b111111) << 2
+ r = ((rgb >> 11) & 0b11111) << 3
+ a = 255
+ else:
+ assert False
+ im.putpixel((x, y), (r, g, b, a))
+ elif pixfmt in ["YUV422-CbYCrY", "YUV422-YCbYCr"]:
+ for y in range(jpeg_H // decode_scale):
+ for x in range(0, jpeg_W // decode_scale, 2):
+ block = output_data[
+ y*surface_stride + x*BYTESPP:
+ y*surface_stride + (x+2)*BYTESPP]
+
+ if pixfmt == "YUV422-CbYCrY":
+ cb, y0, cr, y1 = block
+ elif pixfmt == "YUV422-YCbYCr":
+ y0, cb, y1, cr = block
+
+ r0, g0, b0 = yuv2rgb(y0, cb, cr)
+ r1, g1, b1 = yuv2rgb(y1, cb, cr)
+
+ im.putpixel((x, y), (r0, g0, b0, 255))
+ # XXX this really needs some fixing
+ if x+1 < jpeg_W // decode_scale:
+ im.putpixel((x+1, y), (r1, g1, b1, 255))
+ elif pixfmt == "YUV422-planar":
+ for y in range(jpeg_H // decode_scale):
+ for x in range(jpeg_W // decode_scale):
+ y_ = output_data[y*surface_stride + x]
+ cb = output_data[surface_P1_off + y*surface_P1_stride + x&~1]
+ cr = output_data[surface_P1_off + y*surface_P1_stride + (x&~1)+1]
+
+ r, g, b = yuv2rgb(y_, cb, cr)
+
+ im.putpixel((x, y), (r, g, b, 255))
+ elif pixfmt == "YUV420-planar":
+ for y in range(jpeg_H // decode_scale):
+ for x in range(jpeg_W // decode_scale):
+ y_ = output_data[y*surface_stride + x]
+ cb = output_data[surface_P1_off + (y//2)*surface_P1_stride + x&~1]
+ cr = output_data[surface_P1_off + (y//2)*surface_P1_stride + (x&~1)+1]
+
+ r, g, b = yuv2rgb(y_, cb, cr)
+
+ im.putpixel((x, y), (r, g, b, 255))
+ elif pixfmt == "YUV444-planar":
+ for y in range(jpeg_H // decode_scale):
+ for x in range(jpeg_W // decode_scale):
+ y_ = output_data[y*surface_stride + x]
+ cb = output_data[surface_P1_off + y*surface_P1_stride + x*2]
+ cr = output_data[surface_P1_off + y*surface_P1_stride + x*2+1]
+
+ r, g, b = yuv2rgb(y_, cb, cr)
+
+ im.putpixel((x, y), (r, g, b, 255))
+ else:
+ assert False
+ im.save(args.output)
+
+if args.encode:
+ iface.writemem(input_buf_phys, image_data)
+ iface.writemem(input_buf_phys + surface_P1_off, image_data_P1)
+ print("Pixel data uploaded")
+
+ jpeg.MODE = 0x17f
+ jpeg.REG_0x38 = 0x1 # if not set nothing happens
+ jpeg.REG_0x2c = 0x1 # if not set only header is output
+ jpeg.REG_0x34 = 0x0 # if set output is a JPEG but weird with no footer
+
+ if args.encode_subsampling == '444':
+ jpeg.CODEC.set(CODEC=E_CODEC._444)
+ elif args.encode_subsampling == '422':
+ jpeg.CODEC.set(CODEC=E_CODEC._422)
+ elif args.encode_subsampling == '420':
+ jpeg.CODEC.set(CODEC=E_CODEC._420)
+ elif args.encode_subsampling == '400':
+ jpeg.CODEC.set(CODEC=E_CODEC._400)
+ else:
+ assert False
+
+ if BYTESPP_P1 != 0:
+ jpeg.PX_USE_PLANE1 = 1
+ jpeg.PX_PLANE1_WIDTH = im_W*BYTESPP_P1 - 1
+ jpeg.PX_PLANE1_HEIGHT = im_H // P1_DIVH - 1
+ else:
+ jpeg.PX_USE_PLANE1 = 0
+ jpeg.PX_PLANE1_WIDTH = 0xffffffff
+ jpeg.PX_PLANE1_HEIGHT = 0xffffffff
+ jpeg.PX_PLANE0_WIDTH = im_W*BYTESPP - 1
+ jpeg.PX_PLANE0_HEIGHT = im_H - 1
+ jpeg.TIMEOUT = 266000000
+
+ jpeg.PX_TILES_W = divroundup(im_W, macroblock_W)
+ jpeg.PX_TILES_H = divroundup(im_H, macroblock_H)
+ if pixfmt in ['RGB888', 'RGB101010', 'YUV10']:
+ if args.encode_subsampling == '444' or args.encode_subsampling == '400':
+ jpeg.PX_PLANE0_TILING_H = 4
+ jpeg.PX_PLANE0_TILING_V = 8
+ jpeg.PX_PLANE1_TILING_H = 1
+ jpeg.PX_PLANE1_TILING_V = 1
+ elif args.encode_subsampling == '422':
+ jpeg.PX_PLANE0_TILING_H = 8
+ jpeg.PX_PLANE0_TILING_V = 8
+ jpeg.PX_PLANE1_TILING_H = 1
+ jpeg.PX_PLANE1_TILING_V = 1
+ elif args.encode_subsampling == '420':
+ jpeg.PX_PLANE0_TILING_H = 8
+ jpeg.PX_PLANE0_TILING_V = 16
+ jpeg.PX_PLANE1_TILING_H = 0
+ jpeg.PX_PLANE1_TILING_V = 0
+ else:
+ assert False
+ elif pixfmt == 'RGB565':
+ if args.encode_subsampling == '444' or args.encode_subsampling == '400':
+ jpeg.PX_PLANE0_TILING_H = 2
+ jpeg.PX_PLANE0_TILING_V = 8
+ jpeg.PX_PLANE1_TILING_H = 1
+ jpeg.PX_PLANE1_TILING_V = 1
+ elif args.encode_subsampling == '422':
+ jpeg.PX_PLANE0_TILING_H = 4
+ jpeg.PX_PLANE0_TILING_V = 8
+ jpeg.PX_PLANE1_TILING_H = 1
+ jpeg.PX_PLANE1_TILING_V = 1
+ elif args.encode_subsampling == '420':
+ jpeg.PX_PLANE0_TILING_H = 4
+ jpeg.PX_PLANE0_TILING_V = 16
+ jpeg.PX_PLANE1_TILING_H = 0
+ jpeg.PX_PLANE1_TILING_V = 0
+ else:
+ assert False
+ elif pixfmt == 'YUV-linear':
+ if args.encode_subsampling == '444' or args.encode_subsampling == '400':
+ jpeg.PX_PLANE0_TILING_H = 2
+ jpeg.PX_PLANE0_TILING_V = 8
+ jpeg.PX_PLANE1_TILING_H = 1
+ jpeg.PX_PLANE1_TILING_V = 1
+ elif args.encode_subsampling == '422':
+ jpeg.PX_PLANE0_TILING_H = 4
+ jpeg.PX_PLANE0_TILING_V = 8
+ jpeg.PX_PLANE1_TILING_H = 1
+ jpeg.PX_PLANE1_TILING_V = 1
+ elif args.encode_subsampling == '420':
+ jpeg.PX_PLANE0_TILING_H = 4
+ jpeg.PX_PLANE0_TILING_V = 16
+ jpeg.PX_PLANE1_TILING_H = 0
+ jpeg.PX_PLANE1_TILING_V = 0
+ else:
+ assert False
+ elif pixfmt == 'YUV444-planar':
+ if args.encode_subsampling == '444' or args.encode_subsampling == '400':
+ jpeg.PX_PLANE0_TILING_H = 1
+ jpeg.PX_PLANE0_TILING_V = 8
+ jpeg.PX_PLANE1_TILING_H = 2
+ jpeg.PX_PLANE1_TILING_V = 8
+ elif args.encode_subsampling == '422':
+ jpeg.PX_PLANE0_TILING_H = 2
+ jpeg.PX_PLANE0_TILING_V = 8
+ jpeg.PX_PLANE1_TILING_H = 4
+ jpeg.PX_PLANE1_TILING_V = 8
+ elif args.encode_subsampling == '420':
+ jpeg.PX_PLANE0_TILING_H = 2
+ jpeg.PX_PLANE0_TILING_V = 16
+ jpeg.PX_PLANE1_TILING_H = 4
+ jpeg.PX_PLANE1_TILING_V = 16
+ else:
+ assert False
+ elif pixfmt == 'YUV422-planar':
+ if args.encode_subsampling == '444' or args.encode_subsampling == '400':
+ jpeg.PX_PLANE0_TILING_H = 1
+ jpeg.PX_PLANE0_TILING_V = 8
+ jpeg.PX_PLANE1_TILING_H = 1
+ jpeg.PX_PLANE1_TILING_V = 8
+ elif args.encode_subsampling == '422':
+ jpeg.PX_PLANE0_TILING_H = 2
+ jpeg.PX_PLANE0_TILING_V = 8
+ jpeg.PX_PLANE1_TILING_H = 2
+ jpeg.PX_PLANE1_TILING_V = 8
+ elif args.encode_subsampling == '420':
+ jpeg.PX_PLANE0_TILING_H = 2
+ jpeg.PX_PLANE0_TILING_V = 16
+ jpeg.PX_PLANE1_TILING_H = 2
+ jpeg.PX_PLANE1_TILING_V = 16
+ else:
+ assert False
+ elif pixfmt == 'YUV420-planar':
+ if args.encode_subsampling == '444' or args.encode_subsampling == '400':
+ jpeg.PX_PLANE0_TILING_H = 1
+ jpeg.PX_PLANE0_TILING_V = 8
+ jpeg.PX_PLANE1_TILING_H = 1
+ jpeg.PX_PLANE1_TILING_V = 4
+ elif args.encode_subsampling == '422':
+ jpeg.PX_PLANE0_TILING_H = 2
+ jpeg.PX_PLANE0_TILING_V = 8
+ jpeg.PX_PLANE1_TILING_H = 2
+ jpeg.PX_PLANE1_TILING_V = 4
+ elif args.encode_subsampling == '420':
+ jpeg.PX_PLANE0_TILING_H = 2
+ jpeg.PX_PLANE0_TILING_V = 16
+ jpeg.PX_PLANE1_TILING_H = 2
+ jpeg.PX_PLANE1_TILING_V = 8
+ else:
+ assert False
+ else:
+ assert False
+ jpeg.PX_PLANE0_STRIDE = surface_stride
+ jpeg.PX_PLANE1_STRIDE = surface_P1_stride
+
+ if pixfmt in ['RGB888', 'RGB101010', 'RGB565', 'YUV10', 'YUV444-planar']:
+ if args.encode_subsampling in ['422', '420']:
+ jpeg.CHROMA_HALVE_H_TYPE1 = 1
+ if args.encode_subsampling == '420':
+ jpeg.CHROMA_HALVE_V_TYPE1 = 1
+ elif pixfmt in ['YUV-linear', 'YUV422-planar']:
+ if args.encode_subsampling == '420':
+ jpeg.CHROMA_HALVE_V_TYPE1 = 1
+ elif pixfmt == 'YUV420-planar':
+ if args.encode_subsampling in ['422', '444']:
+ jpeg.CHROMA_DOUBLE_V = 1
+ else:
+ assert False
+
+ # none of this seems to affect anything????
+ jpeg.REG_0x94 = 0xc # c/2 for 444; 8/2 for 422; 3/1 for 411; b/2 for 400
+ jpeg.REG_0x98 = 0x2
+ jpeg.REG_0x20c = im_W
+ jpeg.REG_0x210 = im_H
+
+ if pixfmt in ['RGB888', 'RGB101010', 'RGB565']:
+ jpeg.CONVERT_COLOR_SPACE = 1
+ jpeg.MATRIX_MULT[0].val = 0x4d
+ jpeg.MATRIX_MULT[1].val = 0x96
+ jpeg.MATRIX_MULT[2].val = 0x1d
+ jpeg.MATRIX_MULT[3].val = 0xffffffd5
+ jpeg.MATRIX_MULT[4].val = 0xffffffab
+ jpeg.MATRIX_MULT[5].val = 0x80
+ jpeg.MATRIX_MULT[6].val = 0x80
+ jpeg.MATRIX_MULT[7].val = 0xffffff95
+ jpeg.MATRIX_MULT[8].val = 0xffffffeb
+ jpeg.MATRIX_MULT[9].val = 0x0
+ jpeg.MATRIX_MULT[10].val = 0x80
+
+ if pixfmt == 'RGB888':
+ jpeg.ENCODE_PIXEL_FORMAT.set(FORMAT=E_ENCODE_PIXEL_FORMAT.RGB888)
+ elif pixfmt == 'RGB101010':
+ jpeg.ENCODE_PIXEL_FORMAT.set(FORMAT=E_ENCODE_PIXEL_FORMAT.RGB101010)
+ elif pixfmt == 'RGB565':
+ jpeg.ENCODE_PIXEL_FORMAT.set(FORMAT=E_ENCODE_PIXEL_FORMAT.RGB565)
+ elif pixfmt == 'YUV10':
+ jpeg.ENCODE_PIXEL_FORMAT.set(FORMAT=E_ENCODE_PIXEL_FORMAT.YUV10_linear)
+ elif pixfmt == 'YUV-linear':
+ jpeg.ENCODE_PIXEL_FORMAT.set(FORMAT=E_ENCODE_PIXEL_FORMAT.YUV_linear)
+ elif pixfmt in ['YUV444-planar', 'YUV422-planar', 'YUV420-planar']:
+ jpeg.ENCODE_PIXEL_FORMAT.set(FORMAT=E_ENCODE_PIXEL_FORMAT.YUV_planar)
+ else:
+ assert False
+ if pixfmt == 'YUV-linear':
+ jpeg.ENCODE_COMPONENT0_POS = 0
+ jpeg.ENCODE_COMPONENT1_POS = 1
+ jpeg.ENCODE_COMPONENT2_POS = 3
+ jpeg.ENCODE_COMPONENT3_POS = 2
+ elif pixfmt in ['YUV422-planar', 'YUV420-planar', 'YUV444-planar']:
+ jpeg.ENCODE_COMPONENT0_POS = 0
+ jpeg.ENCODE_COMPONENT1_POS = 0
+ jpeg.ENCODE_COMPONENT2_POS = 1
+ jpeg.ENCODE_COMPONENT3_POS = 3
+ else:
+ jpeg.ENCODE_COMPONENT0_POS = 0
+ jpeg.ENCODE_COMPONENT1_POS = 1
+ jpeg.ENCODE_COMPONENT2_POS = 2
+ jpeg.ENCODE_COMPONENT3_POS = 3
+
+ jpeg.INPUT_START1 = input_buf_iova
+ jpeg.INPUT_START2 = input_buf_iova + surface_P1_off
+ jpeg.INPUT_END = input_buf_iova + input_mem_sz + 7 # NOTE +7
+ jpeg.OUTPUT_START1 = output_buf_iova
+ jpeg.OUTPUT_START2 = 0xdeadbeef
+ jpeg.OUTPUT_END = output_buf_iova + output_mem_sz
+
+ jpeg.REG_0x118 = 0x1
+ jpeg.REG_0x11c = 0x0
+
+ jpeg.ENABLE_RST_LOGGING = args.encode_rst_interval is not None
+
+ jpeg.MODE = 0x16f
+ if args.encode_subsampling == '444':
+ jpeg_subsampling = E_JPEG_IO_FLAGS_SUBSAMPLING._444
+ elif args.encode_subsampling == '422':
+ jpeg_subsampling = E_JPEG_IO_FLAGS_SUBSAMPLING._422
+ elif args.encode_subsampling == '420':
+ jpeg_subsampling = E_JPEG_IO_FLAGS_SUBSAMPLING._420
+ elif args.encode_subsampling == '400':
+ jpeg_subsampling = E_JPEG_IO_FLAGS_SUBSAMPLING._400
+ else:
+ assert False
+ jpeg.JPEG_IO_FLAGS.set(
+ OUTPUT_8BYTE_CHUNKS_CORRECTLY=1,
+ OUTPUT_MACROBLOCKS_UNFLIPPED_H=1,
+ SUBSAMPLING_MODE=jpeg_subsampling
+ )
+ jpeg.JPEG_WIDTH = im_W
+ jpeg.JPEG_HEIGHT = im_H
+ if args.encode_rst_interval is not None:
+ jpeg.RST_INTERVAL = args.encode_rst_interval
+ else:
+ jpeg.RST_INTERVAL = 0
+ jpeg.JPEG_OUTPUT_FLAGS = 0
+
+ jpeg.QTBL[0].val = 0xa06e64a0
+ jpeg.QTBL[1].val = 0xf0ffffff
+ jpeg.QTBL[2].val = 0x78788cbe
+ jpeg.QTBL[3].val = 0xffffffff
+ jpeg.QTBL[4].val = 0x8c82a0f0
+ jpeg.QTBL[5].val = 0xffffffff
+ jpeg.QTBL[6].val = 0x8caadcff
+ jpeg.QTBL[7].val = 0xffffffff
+ jpeg.QTBL[8].val = 0xb4dcffff
+ jpeg.QTBL[9].val = 0xffffffff
+ jpeg.QTBL[10].val = 0xf0ffffff
+ jpeg.QTBL[11].val = 0xffffffff
+ jpeg.QTBL[12].val = 0xffffffff
+ jpeg.QTBL[13].val = 0xffffffff
+ jpeg.QTBL[14].val = 0xffffffff
+ jpeg.QTBL[15].val = 0xffffffff
+
+ jpeg.QTBL[16].val = 0xaab4f0ff
+ jpeg.QTBL[17].val = 0xffffffff
+ jpeg.QTBL[18].val = 0xb4d2ffff
+ jpeg.QTBL[19].val = 0xffffffff
+ jpeg.QTBL[20].val = 0xf0ffffff
+ jpeg.QTBL[21].val = 0xffffffff
+ jpeg.QTBL[22].val = 0xffffffff
+ jpeg.QTBL[23].val = 0xffffffff
+ jpeg.QTBL[24].val = 0xffffffff
+ jpeg.QTBL[25].val = 0xffffffff
+ jpeg.QTBL[26].val = 0xffffffff
+ jpeg.QTBL[27].val = 0xffffffff
+ jpeg.QTBL[28].val = 0xffffffff
+ jpeg.QTBL[29].val = 0xffffffff
+ jpeg.QTBL[30].val = 0xffffffff
+ jpeg.QTBL[31].val = 0xffffffff
+
+ jpeg.QTBL[32].val = 0x01010201
+ jpeg.QTBL[33].val = 0x01020202
+ jpeg.QTBL[34].val = 0x02030202
+ jpeg.QTBL[35].val = 0x03030604
+ jpeg.QTBL[36].val = 0x03030303
+ jpeg.QTBL[37].val = 0x07050804
+ jpeg.QTBL[38].val = 0x0608080a
+ jpeg.QTBL[39].val = 0x0908070b
+ jpeg.QTBL[40].val = 0x080a0e0d
+ jpeg.QTBL[41].val = 0x0b0a0a0c
+ jpeg.QTBL[42].val = 0x0a08080b
+ jpeg.QTBL[43].val = 0x100c0c0d
+ jpeg.QTBL[44].val = 0x0f0f0f0f
+ jpeg.QTBL[45].val = 0x090b1011
+ jpeg.QTBL[46].val = 0x0f0e110d
+ jpeg.QTBL[47].val = 0x0e0e0e01
+
+ jpeg.QTBL[48].val = 0x04040405
+ jpeg.QTBL[49].val = 0x04050905
+ jpeg.QTBL[50].val = 0x05090f0a
+ jpeg.QTBL[51].val = 0x080a0f1a
+ jpeg.QTBL[52].val = 0x13090913
+ jpeg.QTBL[53].val = 0x1a1a1a1a
+ jpeg.QTBL[54].val = 0x0d1a1a1a
+ jpeg.QTBL[55].val = 0x1a1a1a1a
+ jpeg.QTBL[56].val = 0x1a1a1a1a
+ jpeg.QTBL[57].val = 0x1a1a1a1a
+ jpeg.QTBL[58].val = 0x1a1a1a1a
+ jpeg.QTBL[59].val = 0x1a1a1a1a
+ jpeg.QTBL[60].val = 0x1a1a1a1a
+ jpeg.QTBL[61].val = 0x1a1a1a1a
+ jpeg.QTBL[62].val = 0x1a1a1a1a
+ jpeg.QTBL[63].val = 0x1a1a1a1a
+
+ jpeg.HUFFMAN_TABLE.val = 0x3c
+ jpeg.QTBL_SEL.val = 0xff
+ jpeg.REG_0x0.val = 0x1
+ jpeg.REG_0x1004.val = 0x1
+
+ # FIXME: we don't actually know when it's done
+ time.sleep(1)
+
+ print(jpeg.STATUS.reg)
+ print(jpeg.PERFCOUNTER.reg)
+ jpeg_out_sz = jpeg.COMPRESSED_BYTES.val
+ print(f"JPEG output is {jpeg_out_sz} bytes")
+
+ rst_log_n = jpeg.RST_LOG_ENTRIES.val
+ for i in range(rst_log_n):
+ print(f"RST log[{i}] = 0x{jpeg.RSTLOG[i].val:X}")
+
+ output_data = iface.readmem(output_buf_phys, output_mem_sz)
+ if args.raw_output is not None:
+ with open(args.raw_output, 'wb') as f:
+ f.write(output_data)
+ with open(args.output, 'wb') as f:
+ f.write(output_data[:jpeg_out_sz])