Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

media: aspeed: Allow to capture from SoC display (GFX)

ASPEED BMC IC has 2 different display engines. Please find AST2600's
datasheet to get detailed information.

1. VGA on PCIe
2. SoC Display (GFX)

By default, video engine (VE) will capture video from VGA. This patch
adds an option to capture video from GFX with standard ioctl,
vidioc_s_input.

An enum, aspeed_video_input, is added for this purpose.
enum aspeed_video_input {
VIDEO_INPUT_VGA = 0,
VIDEO_INPUT_GFX,
VIDEO_INPUT_MAX
};

To test this feature, you will need to enable GFX first. Please refer to
ASPEED's SDK_User_Guide, 6.3.x Soc Display driver, for more information.
In your application, you will need to use v4l2 ioctl, VIDIOC_S_INPUT, as
below to select before start streaming.

int rc;
struct v4l2_input input;

input.index = VIDEO_INPUT_GFX;
rc = ioctl(fd, VIDIOC_S_INPUT, &input);
if (rc < 0)
{
...
}

Link: https://github.com/AspeedTech-BMC/openbmc/releases
Signed-off-by: Jammy Huang <jammy_huang@aspeedtech.com>
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
[hverkuil: split up three overly long lines]

authored by

Jammy Huang and committed by
Hans Verkuil
039b9302 9842379b

+178 -28
+171 -28
drivers/media/platform/aspeed/aspeed-video.c
··· 4 4 5 5 #include <linux/atomic.h> 6 6 #include <linux/bitfield.h> 7 + #include <linux/cleanup.h> 7 8 #include <linux/clk.h> 8 9 #include <linux/delay.h> 9 10 #include <linux/device.h> ··· 26 25 #include <linux/workqueue.h> 27 26 #include <linux/debugfs.h> 28 27 #include <linux/ktime.h> 28 + #include <linux/regmap.h> 29 + #include <linux/mfd/syscon.h> 29 30 #include <media/v4l2-ctrls.h> 30 31 #include <media/v4l2-dev.h> 31 32 #include <media/v4l2-device.h> ··· 206 203 #define VE_MEM_RESTRICT_START 0x310 207 204 #define VE_MEM_RESTRICT_END 0x314 208 205 206 + /* SCU's registers */ 207 + #define SCU_MISC_CTRL 0xC0 208 + #define SCU_DPLL_SOURCE BIT(20) 209 + 210 + /* GFX's registers */ 211 + #define GFX_CTRL 0x60 212 + #define GFX_CTRL_ENABLE BIT(0) 213 + #define GFX_CTRL_FMT GENMASK(9, 7) 214 + 215 + #define GFX_H_DISPLAY 0x70 216 + #define GFX_H_DISPLAY_DE GENMASK(28, 16) 217 + #define GFX_H_DISPLAY_TOTAL GENMASK(12, 0) 218 + 219 + #define GFX_V_DISPLAY 0x78 220 + #define GFX_V_DISPLAY_DE GENMASK(27, 16) 221 + #define GFX_V_DISPLAY_TOTAL GENMASK(11, 0) 222 + 223 + #define GFX_DISPLAY_ADDR 0x80 224 + 209 225 /* 210 226 * VIDEO_MODE_DETECT_DONE: a flag raised if signal lock 211 227 * VIDEO_RES_CHANGE: a flag raised if res_change work on-going ··· 284 262 /* 285 263 * struct aspeed_video - driver data 286 264 * 265 + * version: holds the version of aspeed SoC 287 266 * res_work: holds the delayed_work for res-detection if unlock 288 267 * buffers: holds the list of buffer queued from user 289 268 * flags: holds the state of video ··· 296 273 * yuv420: a flag raised if JPEG subsampling is 420 297 274 * format: holds the video format 298 275 * hq_mode: a flag raised if HQ is enabled. Only for VIDEO_FMT_ASPEED 276 + * input: holds the video input 299 277 * frame_rate: holds the frame_rate 300 278 * jpeg_quality: holds jpeq's quality (0~11) 301 279 * jpeg_hq_quality: holds hq's quality (1~12) only if hq_mode enabled ··· 322 298 struct video_device vdev; 323 299 struct mutex video_lock; /* v4l2 and videobuf2 lock */ 324 300 301 + struct regmap *scu; 302 + struct regmap *gfx; 303 + u32 version; 325 304 u32 jpeg_mode; 326 305 u32 comp_size_read; 327 306 ··· 343 316 bool yuv420; 344 317 enum aspeed_video_format format; 345 318 bool hq_mode; 319 + enum aspeed_video_input input; 346 320 unsigned int frame_rate; 347 321 unsigned int jpeg_quality; 348 322 unsigned int jpeg_hq_quality; ··· 359 331 #define to_aspeed_video(x) container_of((x), struct aspeed_video, v4l2_dev) 360 332 361 333 struct aspeed_video_config { 334 + u32 version; 362 335 u32 jpeg_mode; 363 336 u32 comp_size_read; 364 337 }; 365 338 366 339 static const struct aspeed_video_config ast2400_config = { 340 + .version = 4, 367 341 .jpeg_mode = AST2400_VE_SEQ_CTRL_JPEG_MODE, 368 342 .comp_size_read = AST2400_VE_COMP_SIZE_READ_BACK, 369 343 }; 370 344 371 345 static const struct aspeed_video_config ast2500_config = { 346 + .version = 5, 372 347 .jpeg_mode = AST2500_VE_SEQ_CTRL_JPEG_MODE, 373 348 .comp_size_read = AST2400_VE_COMP_SIZE_READ_BACK, 374 349 }; 375 350 376 351 static const struct aspeed_video_config ast2600_config = { 352 + .version = 6, 377 353 .jpeg_mode = AST2500_VE_SEQ_CTRL_JPEG_MODE, 378 354 .comp_size_read = AST2600_VE_COMP_SIZE_READ_BACK, 379 355 }; ··· 517 485 518 486 static const char * const format_str[] = {"Standard JPEG", 519 487 "Aspeed JPEG"}; 488 + static const char * const input_str[] = {"HOST VGA", "BMC GFX"}; 520 489 521 490 static unsigned int debug; 522 491 ··· 640 607 &video->bcd.dma, video->bcd.size); 641 608 } else if (!bcd_buf_need && video->bcd.size) { 642 609 aspeed_video_free_buf(video, &video->bcd); 610 + } 611 + 612 + if (video->input == VIDEO_INPUT_GFX) { 613 + u32 val; 614 + 615 + // update input buffer address as gfx's 616 + regmap_read(video->gfx, GFX_DISPLAY_ADDR, &val); 617 + aspeed_video_write(video, VE_TGS_0, val); 643 618 } 644 619 645 620 spin_lock_irqsave(&video->lock, flags); ··· 1067 1026 } 1068 1027 } 1069 1028 1029 + static void aspeed_video_get_resolution_gfx(struct aspeed_video *video, 1030 + struct v4l2_bt_timings *det) 1031 + { 1032 + u32 h_val, v_val; 1033 + 1034 + regmap_read(video->gfx, GFX_H_DISPLAY, &h_val); 1035 + regmap_read(video->gfx, GFX_V_DISPLAY, &v_val); 1036 + 1037 + det->width = FIELD_GET(GFX_H_DISPLAY_DE, h_val) + 1; 1038 + det->height = FIELD_GET(GFX_V_DISPLAY_DE, v_val) + 1; 1039 + video->v4l2_input_status = 0; 1040 + } 1041 + 1070 1042 #define res_check(v) test_and_clear_bit(VIDEO_MODE_DETECT_DONE, &(v)->flags) 1071 1043 1072 - static void aspeed_video_get_resolution(struct aspeed_video *video) 1044 + static void aspeed_video_get_resolution_vga(struct aspeed_video *video, 1045 + struct v4l2_bt_timings *det) 1073 1046 { 1074 1047 bool invalid_resolution = true; 1075 1048 int rc; ··· 1091 1036 u32 mds; 1092 1037 u32 src_lr_edge; 1093 1038 u32 src_tb_edge; 1094 - struct v4l2_bt_timings *det = &video->detected_timings; 1095 1039 1096 1040 det->width = MIN_WIDTH; 1097 1041 det->height = MIN_HEIGHT; ··· 1167 1113 1168 1114 aspeed_video_get_timings(video, det); 1169 1115 1170 - /* 1171 - * Enable mode-detect watchdog, resolution-change watchdog and 1172 - * automatic compression after frame capture. 1173 - */ 1116 + /* Enable mode-detect watchdog, resolution-change watchdog */ 1174 1117 aspeed_video_update(video, VE_INTERRUPT_CTRL, 0, 1175 1118 VE_INTERRUPT_MODE_DETECT_WD); 1176 - aspeed_video_update(video, VE_SEQ_CTRL, 0, 1177 - VE_SEQ_CTRL_AUTO_COMP | VE_SEQ_CTRL_EN_WATCHDOG); 1119 + aspeed_video_update(video, VE_SEQ_CTRL, 0, VE_SEQ_CTRL_EN_WATCHDOG); 1120 + } 1121 + 1122 + static void aspeed_video_get_resolution(struct aspeed_video *video) 1123 + { 1124 + struct v4l2_bt_timings *det = &video->detected_timings; 1125 + 1126 + if (video->input == VIDEO_INPUT_GFX) 1127 + aspeed_video_get_resolution_gfx(video, det); 1128 + else 1129 + aspeed_video_get_resolution_vga(video, det); 1178 1130 1179 1131 v4l2_dbg(1, debug, &video->v4l2_dev, "Got resolution: %dx%d\n", 1180 1132 det->width, det->height); ··· 1216 1156 aspeed_video_write(video, VE_SRC_SCANLINE_OFFSET, act->width * 4); 1217 1157 1218 1158 /* Don't use direct mode below 1024 x 768 (irqs don't fire) */ 1219 - if (size < DIRECT_FETCH_THRESHOLD) { 1159 + if (video->input == VIDEO_INPUT_VGA && size < DIRECT_FETCH_THRESHOLD) { 1220 1160 v4l2_dbg(1, debug, &video->v4l2_dev, "Capture: Sync Mode\n"); 1221 1161 aspeed_video_write(video, VE_TGS_0, 1222 1162 FIELD_PREP(VE_TGS_FIRST, ··· 1231 1171 VE_CTRL_INT_DE | VE_CTRL_DIRECT_FETCH, 1232 1172 VE_CTRL_INT_DE); 1233 1173 } else { 1174 + u32 ctrl, val, bpp; 1175 + 1234 1176 v4l2_dbg(1, debug, &video->v4l2_dev, "Capture: Direct Mode\n"); 1177 + ctrl = VE_CTRL_DIRECT_FETCH; 1178 + if (video->input == VIDEO_INPUT_GFX) { 1179 + regmap_read(video->gfx, GFX_CTRL, &val); 1180 + bpp = FIELD_GET(GFX_CTRL_FMT, val) ? 32 : 16; 1181 + if (bpp == 16) 1182 + ctrl |= VE_CTRL_INT_DE; 1183 + aspeed_video_write(video, VE_TGS_1, act->width * (bpp >> 3)); 1184 + } 1235 1185 aspeed_video_update(video, VE_CTRL, 1236 1186 VE_CTRL_INT_DE | VE_CTRL_DIRECT_FETCH, 1237 - VE_CTRL_DIRECT_FETCH); 1187 + ctrl); 1238 1188 } 1239 1189 1240 1190 size *= 4; ··· 1277 1207 aspeed_video_free_buf(video, &video->srcs[0]); 1278 1208 } 1279 1209 1210 + /* 1211 + * Update relative parameters when timing changed. 1212 + * 1213 + * @video: the struct of aspeed_video 1214 + * @timings: the new timings 1215 + */ 1216 + static void aspeed_video_update_timings(struct aspeed_video *video, struct v4l2_bt_timings *timings) 1217 + { 1218 + video->active_timings = *timings; 1219 + aspeed_video_set_resolution(video); 1220 + 1221 + video->pix_fmt.width = timings->width; 1222 + video->pix_fmt.height = timings->height; 1223 + video->pix_fmt.sizeimage = video->max_compressed_size; 1224 + } 1225 + 1280 1226 static void aspeed_video_update_regs(struct aspeed_video *video) 1281 1227 { 1282 1228 u8 jpeg_hq_quality = clamp((int)video->jpeg_hq_quality - 1, 0, ··· 1305 1219 u32 ctrl = 0; 1306 1220 u32 seq_ctrl = 0; 1307 1221 1222 + v4l2_dbg(1, debug, &video->v4l2_dev, "input(%s)\n", 1223 + input_str[video->input]); 1308 1224 v4l2_dbg(1, debug, &video->v4l2_dev, "framerate(%d)\n", 1309 1225 video->frame_rate); 1310 1226 v4l2_dbg(1, debug, &video->v4l2_dev, "jpeg format(%s) subsample(%s)\n", ··· 1321 1233 aspeed_video_update(video, VE_BCD_CTRL, 0, VE_BCD_CTRL_EN_BCD); 1322 1234 else 1323 1235 aspeed_video_update(video, VE_BCD_CTRL, VE_BCD_CTRL_EN_BCD, 0); 1236 + 1237 + if (video->input == VIDEO_INPUT_VGA) 1238 + ctrl |= VE_CTRL_AUTO_OR_CURSOR; 1324 1239 1325 1240 if (video->frame_rate) 1326 1241 ctrl |= FIELD_PREP(VE_CTRL_FRC, video->frame_rate); ··· 1343 1252 aspeed_video_update(video, VE_SEQ_CTRL, 1344 1253 video->jpeg_mode | VE_SEQ_CTRL_YUV420, 1345 1254 seq_ctrl); 1346 - aspeed_video_update(video, VE_CTRL, VE_CTRL_FRC, ctrl); 1255 + aspeed_video_update(video, VE_CTRL, 1256 + VE_CTRL_FRC | VE_CTRL_AUTO_OR_CURSOR | 1257 + VE_CTRL_SOURCE, ctrl); 1347 1258 aspeed_video_update(video, VE_COMP_CTRL, 1348 1259 VE_COMP_CTRL_DCT_LUM | VE_COMP_CTRL_DCT_CHR | 1349 1260 VE_COMP_CTRL_EN_HQ | VE_COMP_CTRL_HQ_DCT_LUM | ··· 1373 1280 aspeed_video_write(video, VE_JPEG_ADDR, video->jpeg.dma); 1374 1281 1375 1282 /* Set control registers */ 1283 + aspeed_video_write(video, VE_SEQ_CTRL, VE_SEQ_CTRL_AUTO_COMP); 1376 1284 aspeed_video_write(video, VE_CTRL, ctrl); 1377 1285 aspeed_video_write(video, VE_COMP_CTRL, VE_COMP_CTRL_RSVD); 1378 1286 ··· 1405 1311 aspeed_video_get_resolution(video); 1406 1312 1407 1313 /* Set timings since the device is being opened for the first time */ 1408 - video->active_timings = video->detected_timings; 1409 - aspeed_video_set_resolution(video); 1410 - 1411 - video->pix_fmt.width = video->active_timings.width; 1412 - video->pix_fmt.height = video->active_timings.height; 1413 - video->pix_fmt.sizeimage = video->max_compressed_size; 1314 + aspeed_video_update_timings(video, &video->detected_timings); 1414 1315 } 1415 1316 1416 1317 static void aspeed_video_stop(struct aspeed_video *video) ··· 1490 1401 { 1491 1402 struct aspeed_video *video = video_drvdata(file); 1492 1403 1493 - if (inp->index) 1404 + if (inp->index >= VIDEO_INPUT_MAX) 1494 1405 return -EINVAL; 1495 1406 1496 - strscpy(inp->name, "Host VGA capture", sizeof(inp->name)); 1407 + sprintf(inp->name, "%s capture", input_str[inp->index]); 1497 1408 inp->type = V4L2_INPUT_TYPE_CAMERA; 1498 1409 inp->capabilities = V4L2_IN_CAP_DV_TIMINGS; 1499 1410 inp->status = video->v4l2_input_status; ··· 1503 1414 1504 1415 static int aspeed_video_get_input(struct file *file, void *fh, unsigned int *i) 1505 1416 { 1506 - *i = 0; 1417 + struct aspeed_video *video = video_drvdata(file); 1418 + 1419 + *i = video->input; 1507 1420 1508 1421 return 0; 1509 1422 } 1510 1423 1511 1424 static int aspeed_video_set_input(struct file *file, void *fh, unsigned int i) 1512 1425 { 1513 - if (i) 1426 + struct aspeed_video *video = video_drvdata(file); 1427 + 1428 + if (i >= VIDEO_INPUT_MAX) 1514 1429 return -EINVAL; 1430 + 1431 + if (i == video->input) 1432 + return 0; 1433 + 1434 + if (vb2_is_busy(&video->queue)) 1435 + return -EBUSY; 1436 + 1437 + if (IS_ERR(video->scu)) { 1438 + v4l2_dbg(1, debug, &video->v4l2_dev, 1439 + "%s: scu isn't ready for input-control\n", __func__); 1440 + return -EINVAL; 1441 + } 1442 + 1443 + if (IS_ERR(video->gfx) && i == VIDEO_INPUT_GFX) { 1444 + v4l2_dbg(1, debug, &video->v4l2_dev, 1445 + "%s: gfx isn't ready for GFX input\n", __func__); 1446 + return -EINVAL; 1447 + } 1448 + 1449 + video->input = i; 1450 + 1451 + if (video->version == 6) { 1452 + /* modify dpll source per current input */ 1453 + if (video->input == VIDEO_INPUT_VGA) 1454 + regmap_update_bits(video->scu, SCU_MISC_CTRL, 1455 + SCU_DPLL_SOURCE, 0); 1456 + else 1457 + regmap_update_bits(video->scu, SCU_MISC_CTRL, 1458 + SCU_DPLL_SOURCE, SCU_DPLL_SOURCE); 1459 + } 1460 + 1461 + aspeed_video_update_regs(video); 1462 + 1463 + /* update signal status */ 1464 + aspeed_video_get_resolution(video); 1465 + if (!video->v4l2_input_status) 1466 + aspeed_video_update_timings(video, &video->detected_timings); 1515 1467 1516 1468 return 0; 1517 1469 } ··· 1657 1527 if (vb2_is_busy(&video->queue)) 1658 1528 return -EBUSY; 1659 1529 1660 - video->active_timings = timings->bt; 1661 - 1662 - aspeed_video_set_resolution(video); 1663 - 1664 - video->pix_fmt.width = timings->bt.width; 1665 - video->pix_fmt.height = timings->bt.height; 1666 - video->pix_fmt.sizeimage = video->max_compressed_size; 1530 + aspeed_video_update_timings(video, &timings->bt); 1667 1531 1668 1532 timings->type = V4L2_DV_BT_656_1120; 1669 1533 ··· 2033 1909 val08 = aspeed_video_read(v, VE_CTRL); 2034 1910 if (FIELD_GET(VE_CTRL_DIRECT_FETCH, val08)) { 2035 1911 seq_printf(s, " %-20s:\tDirect fetch\n", "Mode"); 1912 + seq_printf(s, " %-20s:\t%s\n", "Input", input_str[v->input]); 2036 1913 seq_printf(s, " %-20s:\t%s\n", "VGA bpp mode", 2037 1914 FIELD_GET(VE_CTRL_INT_DE, val08) ? "16" : "32"); 2038 1915 } else { ··· 2193 2068 return 0; 2194 2069 } 2195 2070 2071 + /* 2072 + * Get regmap without checking res, such as clk/reset, that could lead to 2073 + * conflict. 2074 + */ 2075 + static struct regmap *aspeed_regmap_lookup(struct device_node *np, const char *property) 2076 + { 2077 + struct device_node *syscon_np __free(device_node) = of_parse_phandle(np, property, 0); 2078 + 2079 + if (!syscon_np) 2080 + return ERR_PTR(-ENODEV); 2081 + 2082 + return device_node_to_regmap(syscon_np); 2083 + } 2084 + 2196 2085 static int aspeed_video_init(struct aspeed_video *video) 2197 2086 { 2198 2087 int irq; 2199 2088 int rc; 2200 2089 struct device *dev = video->dev; 2090 + 2091 + video->scu = aspeed_regmap_lookup(dev->of_node, "aspeed,scu"); 2092 + video->gfx = aspeed_regmap_lookup(dev->of_node, "aspeed,gfx"); 2201 2093 2202 2094 irq = irq_of_parse_and_map(dev->of_node, 0); 2203 2095 if (!irq) { ··· 2307 2165 if (!config) 2308 2166 return -ENODEV; 2309 2167 2168 + video->version = config->version; 2310 2169 video->jpeg_mode = config->jpeg_mode; 2311 2170 video->comp_size_read = config->comp_size_read; 2312 2171
+7
include/uapi/linux/aspeed-video.h
··· 8 8 9 9 #include <linux/v4l2-controls.h> 10 10 11 + /* aspeed video's input types */ 12 + enum aspeed_video_input { 13 + VIDEO_INPUT_VGA = 0, 14 + VIDEO_INPUT_GFX, 15 + VIDEO_INPUT_MAX 16 + }; 17 + 11 18 #define V4L2_CID_ASPEED_HQ_MODE (V4L2_CID_USER_ASPEED_BASE + 1) 12 19 #define V4L2_CID_ASPEED_HQ_JPEG_QUALITY (V4L2_CID_USER_ASPEED_BASE + 2) 13 20