Samsung T560 Camera Support on postmarketOS: A Proven Step-by-Step Guide

samsung-t560-camera-support-postmarketos.md
title Samsung T560 Camera Support on postmarketOS: A Proven Step-by-Step Guide
date
author VahaC
read 11 min read
category Smart home
tags #Linux #SmartHome
Samsung T560 camera support

Samsung T560 camera support on postmarketOS does not exist out of the box: the Galaxy Tab E 9.6 boots a downstream 3.10 kernel whose Spreadtrum camera driver expects an Android HAL that postmarketOS never runs, so /dev/video0 appears but never delivers a frame. This guide walks through the way I added Samsung T560 camera support to my tablet, from the kernel patch set to the first real picture, and finally to a motion detector that wakes the panel backlight. 📷

I use the tablet as a wall panel for my Home Assistant media controller integration (the same project behind my Music Assistant ESP32 media controller), and I wanted the display to switch on when somebody walks up to it. That needs the front camera, and the front camera needs Samsung T560 camera support in the kernel. Everything below is the Samsung T560 camera support procedure I actually ran, on a real SM-T560, over about twenty kernel revisions, with one 16 MiB partition rewritten each time.

What Samsung T560 camera support has to deal with 🔍

Before touching code it helps to know the hardware:

  • SoC: Spreadtrum SC7730 (the kernel calls it sc8830g, config ARCH_SCX30G2), 1.5 GB RAM, kernel 3.10.17 from the pmsourcedump Samsung SM-T560 tree.
  • Front camera: SR200PC20M, a 2 MP YUV sensor on a single MIPI CSI-2 lane at 480 Mbps. It reports chip ID 0xb4.
  • Capture unit: Spreadtrum DCAM. It has three D-PHY receivers, called A, B and C in the driver. Nothing in the kernel tree or the device tree says which one the front camera is wired to.
  • The problem: the stock sprd_dcam driver only configures the sensor when Android’s camera HAL pushes register tables through custom ioctls. Without the HAL there is no sensor init, no MIPI setup and no videobuf2 queue.

The plan: a minimal in-kernel sensor driver, a real V4L2 streaming path in DCAM, the right CSI-2 PHY and byte order, and only then user space.

Prerequisites ✅

You need surprisingly little for Samsung T560 camera support, but every item matters:

  • postmarketOS already installed and booting on the tablet. If you have not done that yet, start with my post on how to install postmarketOS on the Samsung T560.
  • SSH access to the tablet (Dropbear is enough; it has no SFTP, so we will upload through stdin).
  • A Linux machine or WSL with pmbootstrap and the linux-samsung-gtelwifi package from pmaports (it lives under device/archived).
  • TWRP that you can boot into on the tablet, plus ADB on the PC. This is your rescue path if a Samsung T560 camera support kernel refuses to boot.
  • Basic comfort with git apply, patch and dmesg.

Step 1: Back up the boot partition first ⚠️

Samsung T560 camera support lives entirely in the kernel, and the kernel lives in /dev/mmcblk0p20, a 16 MiB boot partition. Everything else stays untouched. Copy that partition and record its checksum before you flash anything:

ssh vahac@tablet "sudo dd if=/dev/mmcblk0p20 bs=1M" > mmcblk0p20-backup.img
sha256sum mmcblk0p20-backup.img

⚠️ Critical: the backup must be exactly 16777216 bytes. Keep its SHA256 in a text file next to it. Every later flash in this guide is verified against a full-partition hash, and this file is what you restore from TWRP if a kernel boot-loops.

Step 2: The Samsung T560 camera support patch set 🧩

The patch set is applied on top of the pmaports package and consists of ten small patches. You do not need to reproduce my revision history, but you do need each piece:

  • videobuf2 capture path in dcam_v4l2.c: VIDIOC_REQBUFS, QBUF, DQBUF and STREAMON on DCAM path 1 with mmap buffers, plus a hardcoded capture context (CSI-2 input, YUV, 8 bit, 1 lane, 800×600 sensor preview scaled to the requested size).
  • A minimal sensor driver sr200pc20m_gtelwifi.c: registers the sensor as a V4L2 subdevice, reuses the board’s existing power sequence from sensor_power_gtel3g.c, enables MCLK at 24 MHz, writes the vendor’s 50 Hz register table, and starts the stream with the wake command 0x0130.
  • MCLK fix: the 3.10 clock framework panics on clk_set_rate(NULL, ...). Acquire the clk_sensor handle from the device tree before setting 24 MHz, and fall back gracefully when the exact rate is refused.
  • CSI-2 PHY selection: the front camera is on PHY C, so sensor_k_mipi_open() must be called with phy_id = 0x04. PHY A and PHY B never see the sensor at all.
  • Capture byte order: the sensor sends UYVY, while the driver’s default programs the capture unit for YUYV. Set the DCAM pattern to DCAM_UYVY before streaming.
  • Quality of life: refuse the unverified GREY (YUV400) output, silence the per-frame DCAM: sof 1 wait printk that otherwise floods the log at 15 lines per second, and expose three root-only module parameters for Samsung T560 camera support experiments.

The two lines of Samsung T560 camera support that took the longest to find are also the shortest:

/* drivers/media/sprd_sensor/gtelwifi/sr200pc20m_gtelwifi.c */
#define SR200PC20M_CSI_PHY_ID   0x04   /* PHY C, not the default PHY A */

/* drivers/media/sprd_dcam/common/dcam_v4l2.c, capture context */
info->yuv_ptn = DCAM_UYVY;             /* sensor bytes arrive U Y V Y */

⚠️ Critical: if you keep PHY A you will see a perfect DCAM configuration and zero frames, and the CSI-2 host status register stays at 0x200 forever (no stop state, no high-speed clock). If you keep YUYV you will get frames whose luma plane is flat grey at 128 while the picture hides in the chroma plane. Neither is a sensor problem.

Keep every change behind CONFIG_MACH_GTELWIFI so the tree still builds for other Spreadtrum boards.

Step 3: Build the kernel with pmbootstrap 🔨

Samsung T560 camera support is built like any other pmaports kernel package. Put the patches next to the APKBUILD, then edit the package:

  1. Bump pkgrel (I went from 8 to 20 over the project; each flash needs a new one).
  2. Add every patch file to the source= list, in order.
  3. Add a matching line to sha512sums= for each patch, or regenerate them with abuild checksum inside the chroot.

Then build and export:

pmbootstrap build linux-samsung-gtelwifi --force --lax
pmbootstrap chroot -r -- apk add --upgrade linux-samsung-gtelwifi=3.10.17-r20
pmbootstrap export /tmp/t560-export
sha256sum /tmp/t560-export/boot.img

Check that the kernel banner inside vmlinuz carries the tag you expect. The package sets KBUILD_BUILD_VERSION to pkgrel + 1, so pkgrel=20 produces #21-postmarketOS. If uname -a shows an older tag after the flash, you flashed the wrong image.

⚠️ Critical: abuild applies patches with GNU patch -p1 and fuzz 2. A hunk with asymmetric context (fewer context lines after than before) is treated as end-of-file and can land in the wrong place without an error. Verify each patch on a clean tree with both git apply --check and patch -p1 --dry-run -F0 before you build.

Step 4: Flash only the boot partition over SSH 💾

Flashing Samsung T560 camera support means rewriting one 16 MiB partition and nothing else. Dropbear has no SFTP, so send the image through stdin and verify it before it goes anywhere near the flash:

ssh vahac@tablet "umask 077; dd of=/tmp/boot.img bs=1M" < boot.img
ssh vahac@tablet "sha256sum /tmp/boot.img"

Now write it, zero-fill the rest of the 16 MiB partition, and compare the full partition hash with the hash of the image padded to 16777216 bytes:

ssh -t vahac@tablet "sudo dd if=/tmp/boot.img of=/dev/mmcblk0p20 bs=2048 conv=fsync && sudo dd if=/dev/zero of=/dev/mmcblk0p20 bs=2048 seek=6718 count=1474 conv=fsync && sudo sha256sum /dev/mmcblk0p20"

seek is the image size divided by 2048 and count is what remains up to 8192 blocks; my images were 13758464 bytes, hence 6718 and 1474. On the PC, { cat boot.img; head -c $((16777216-13758464)) /dev/zero; } | sha256sum must print the same digest.

⚠️ Critical: write nothing but /dev/mmcblk0p20. Do not reboot until the two hashes match. If they do not match, restore the backup from Step 1 immediately with the same dd command.

Step 5: Reboot and check the driver 🔁

After sudo reboot, confirm the new kernel and the parameters that Samsung T560 camera support relies on:

uname -a                                   # expect #21-postmarketOS
ls -l /dev/video0                          # crw-rw---- root video
sudo cat /sys/module/sprd_sensor/parameters/gtelwifi_camera_csi_phy_id   # 4
sudo cat /sys/module/sprd_dcam/parameters/gtelwifi_camera_yuv_pattern    # 2 = UYVY
sudo cat /sys/module/sprd_dcam/parameters/gtelwifi_camera_disable        # N

Make sure your user is in the video group, otherwise the node stays root-only.

Step 6: Capture the first frame 🖼️

This is the moment Samsung T560 camera support either exists or does not. I used a small Python V4L2 client (the same one my motion detector is built on): S_FMT to NV21 320×240, two mmap buffers, STREAMON, then DQBUF in a loop. The standard tool from v4l-utils uses the same ioctls, so this should work as well, although I have not run it on this driver myself:

v4l2-ctl -d /dev/video0 --set-fmt-video=width=320,height=240,pixelformat=NV21 --stream-mmap=2 --stream-count=20 --stream-to=/tmp/frames.nv21
tail -c 115200 /tmp/frames.nv21 > /tmp/frame.nv21

Skip the first frame: the sensor starts mid-frame and the CSI-2 host flags it with a frame-boundary error. Frame twenty, about 1.4 seconds in, has settled exposure. Convert it on the PC:

ffmpeg -f rawvideo -pixel_format nv21 -video_size 320x240 -i frame.nv21 frame.png

A real frame from Samsung T560 camera support looks like a photo: a full luma range (mine spanned 0 to 243), natural low-saturation colour, and recognisable objects. NV21 at 320×240 is exactly 115200 bytes.

Verification: healthy Samsung T560 camera support in dmesg 📋

With everything in place, one camera open produces this sequence:

sensor_sr200pc20m_poweron : OK
camera-r18: DCAM capture YUV pattern=2 (0=YUYV 1=YVYU 2=UYVY 3=VYUY)
camera-r17: opening MIPI on CSI phy_id=0x4 lanes=1 mbps=480
SENSOR: MIPI on, phy 0x4, lane 1, bps 480
sr200pc20m: chip id 0xb4
sr200pc20m: 800x600 preview stream started
DCAM PATH S: 7

The first frame arrives roughly 100 ms after STREAMON, and the sensor keeps delivering about 15 frames per second afterwards. No DCAM timeout. line should appear. If it does, read the next section.

Troubleshooting 🛠️

Every failure mode I hit while bringing up Samsung T560 camera support is on this list:

  • Boot loop after flashing: boot TWRP, adb push the backup, then dd if=/tmp/backup.img of=/dev/block/mmcblk0p20 bs=1048576; sync, and compare sha256sum /dev/block/mmcblk0p20 with the backup hash before rebooting.
  • Kernel panic on the first open: almost certainly a NULL clock handle passed to clk_set_rate(). Get the handle from the DT node of sprd_sensor and check it.
  • DCAM timeout. and zero frames: the capture unit is armed but no start-of-frame arrives. Wrong PHY. Try phy_id 4; PHY A and B stay idle on this board.
  • Frames arrive but look like flat grey with a ghost image in colour: byte order. Switch the capture pattern to UYVY (value 2).
  • S_FMT GREY fails with EINVAL: intended. GREY maps to DCAM YUV400, which I never verified, so the driver refuses it; your application should fall through to NV21.
  • dmesg fills with DCAM: sof 1 wait: normal when your reader is slower than the sensor, but the message must be demoted to a trace, otherwise it floods the ring buffer.
  • Root filesystem almost full: check df -h / before installing v4l-utils on the tablet, and convert frames on the PC.

Turn it into motion detection 👋

This is the part I actually wanted from Samsung T560 camera support. The panel runs a small daemon that reads the NV21 luma plane every 400 ms, averages a 16×12 grid of blocks, compares it with the previous frame and, when enough blocks change over two consecutive frames, sends SIGUSR2 to the process that owns the backlight. That process turns the display on and postpones the idle timeout, so the panel lights up when you approach it. The daemon starts from the Openbox autostart and is enabled in the panel configuration:

[camera]
motion_detection=on
device=auto
width=320
height=240
frame_interval_ms=400
pixel_threshold=12
motion_area_percent=8
motion_frames=2
cooldown_seconds=2

The whole detector is under 600 lines of standard-library Python, which matters on a device with 1.5 GB of RAM and a nearly full root filesystem. The same idea works on my ESP32 media controller panels, just with a PIR sensor instead of a camera.

Wrapping up 🎯

Samsung T560 camera support on postmarketOS comes down to four things: a sensor driver the kernel can run without Android, a real videobuf2 path in DCAM, the correct CSI-2 PHY, and the correct byte order. The rest of this guide is about doing those four things without bricking the boot partition. If you get stuck on the PHY or the byte order, the register snapshots I added to the driver tell you exactly where the Samsung T560 camera support pipeline stops, and that story deserves a post of its own.

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.