ingenic-ipu.c 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998
  1. // SPDX-License-Identifier: GPL-2.0
  2. //
  3. // Ingenic JZ47xx IPU driver
  4. //
  5. // Copyright (C) 2020, Paul Cercueil <[email protected]>
  6. // Copyright (C) 2020, Daniel Silsby <[email protected]>
  7. #include "ingenic-drm.h"
  8. #include "ingenic-ipu.h"
  9. #include <linux/clk.h>
  10. #include <linux/component.h>
  11. #include <linux/gcd.h>
  12. #include <linux/interrupt.h>
  13. #include <linux/module.h>
  14. #include <linux/of.h>
  15. #include <linux/of_device.h>
  16. #include <linux/regmap.h>
  17. #include <linux/time.h>
  18. #include <drm/drm_atomic.h>
  19. #include <drm/drm_atomic_helper.h>
  20. #include <drm/drm_damage_helper.h>
  21. #include <drm/drm_drv.h>
  22. #include <drm/drm_fb_dma_helper.h>
  23. #include <drm/drm_fourcc.h>
  24. #include <drm/drm_framebuffer.h>
  25. #include <drm/drm_gem_atomic_helper.h>
  26. #include <drm/drm_gem_dma_helper.h>
  27. #include <drm/drm_gem_framebuffer_helper.h>
  28. #include <drm/drm_plane.h>
  29. #include <drm/drm_property.h>
  30. #include <drm/drm_vblank.h>
  31. struct ingenic_ipu;
  32. struct soc_info {
  33. const u32 *formats;
  34. size_t num_formats;
  35. bool has_bicubic;
  36. bool manual_restart;
  37. void (*set_coefs)(struct ingenic_ipu *ipu, unsigned int reg,
  38. unsigned int sharpness, bool downscale,
  39. unsigned int weight, unsigned int offset);
  40. };
  41. struct ingenic_ipu_private_state {
  42. struct drm_private_state base;
  43. unsigned int num_w, num_h, denom_w, denom_h;
  44. };
  45. struct ingenic_ipu {
  46. struct drm_plane plane;
  47. struct drm_device *drm;
  48. struct device *dev, *master;
  49. struct regmap *map;
  50. struct clk *clk;
  51. const struct soc_info *soc_info;
  52. bool clk_enabled;
  53. dma_addr_t addr_y, addr_u, addr_v;
  54. struct drm_property *sharpness_prop;
  55. unsigned int sharpness;
  56. struct drm_private_obj private_obj;
  57. };
  58. /* Signed 15.16 fixed-point math (for bicubic scaling coefficients) */
  59. #define I2F(i) ((s32)(i) * 65536)
  60. #define F2I(f) ((f) / 65536)
  61. #define FMUL(fa, fb) ((s32)(((s64)(fa) * (s64)(fb)) / 65536))
  62. #define SHARPNESS_INCR (I2F(-1) / 8)
  63. static inline struct ingenic_ipu *plane_to_ingenic_ipu(struct drm_plane *plane)
  64. {
  65. return container_of(plane, struct ingenic_ipu, plane);
  66. }
  67. static inline struct ingenic_ipu_private_state *
  68. to_ingenic_ipu_priv_state(struct drm_private_state *state)
  69. {
  70. return container_of(state, struct ingenic_ipu_private_state, base);
  71. }
  72. static struct ingenic_ipu_private_state *
  73. ingenic_ipu_get_priv_state(struct ingenic_ipu *priv, struct drm_atomic_state *state)
  74. {
  75. struct drm_private_state *priv_state;
  76. priv_state = drm_atomic_get_private_obj_state(state, &priv->private_obj);
  77. if (IS_ERR(priv_state))
  78. return ERR_CAST(priv_state);
  79. return to_ingenic_ipu_priv_state(priv_state);
  80. }
  81. static struct ingenic_ipu_private_state *
  82. ingenic_ipu_get_new_priv_state(struct ingenic_ipu *priv, struct drm_atomic_state *state)
  83. {
  84. struct drm_private_state *priv_state;
  85. priv_state = drm_atomic_get_new_private_obj_state(state, &priv->private_obj);
  86. if (!priv_state)
  87. return NULL;
  88. return to_ingenic_ipu_priv_state(priv_state);
  89. }
  90. /*
  91. * Apply conventional cubic convolution kernel. Both parameters
  92. * and return value are 15.16 signed fixed-point.
  93. *
  94. * @f_a: Sharpness factor, typically in range [-4.0, -0.25].
  95. * A larger magnitude increases perceived sharpness, but going past
  96. * -2.0 might cause ringing artifacts to outweigh any improvement.
  97. * Nice values on a 320x240 LCD are between -0.75 and -2.0.
  98. *
  99. * @f_x: Absolute distance in pixels from 'pixel 0' sample position
  100. * along horizontal (or vertical) source axis. Range is [0, +2.0].
  101. *
  102. * returns: Weight of this pixel within 4-pixel sample group. Range is
  103. * [-2.0, +2.0]. For moderate (i.e. > -3.0) sharpness factors,
  104. * range is within [-1.0, +1.0].
  105. */
  106. static inline s32 cubic_conv(s32 f_a, s32 f_x)
  107. {
  108. const s32 f_1 = I2F(1);
  109. const s32 f_2 = I2F(2);
  110. const s32 f_3 = I2F(3);
  111. const s32 f_4 = I2F(4);
  112. const s32 f_x2 = FMUL(f_x, f_x);
  113. const s32 f_x3 = FMUL(f_x, f_x2);
  114. if (f_x <= f_1)
  115. return FMUL((f_a + f_2), f_x3) - FMUL((f_a + f_3), f_x2) + f_1;
  116. else if (f_x <= f_2)
  117. return FMUL(f_a, (f_x3 - 5 * f_x2 + 8 * f_x - f_4));
  118. else
  119. return 0;
  120. }
  121. /*
  122. * On entry, "weight" is a coefficient suitable for bilinear mode,
  123. * which is converted to a set of four suitable for bicubic mode.
  124. *
  125. * "weight 512" means all of pixel 0;
  126. * "weight 256" means half of pixel 0 and half of pixel 1;
  127. * "weight 0" means all of pixel 1;
  128. *
  129. * "offset" is increment to next source pixel sample location.
  130. */
  131. static void jz4760_set_coefs(struct ingenic_ipu *ipu, unsigned int reg,
  132. unsigned int sharpness, bool downscale,
  133. unsigned int weight, unsigned int offset)
  134. {
  135. u32 val;
  136. s32 w0, w1, w2, w3; /* Pixel weights at X (or Y) offsets -1,0,1,2 */
  137. weight = clamp_val(weight, 0, 512);
  138. if (sharpness < 2) {
  139. /*
  140. * When sharpness setting is 0, emulate nearest-neighbor.
  141. * When sharpness setting is 1, emulate bilinear.
  142. */
  143. if (sharpness == 0)
  144. weight = weight >= 256 ? 512 : 0;
  145. w0 = 0;
  146. w1 = weight;
  147. w2 = 512 - weight;
  148. w3 = 0;
  149. } else {
  150. const s32 f_a = SHARPNESS_INCR * sharpness;
  151. const s32 f_h = I2F(1) / 2; /* Round up 0.5 */
  152. /*
  153. * Note that always rounding towards +infinity here is intended.
  154. * The resulting coefficients match a round-to-nearest-int
  155. * double floating-point implementation.
  156. */
  157. weight = 512 - weight;
  158. w0 = F2I(f_h + 512 * cubic_conv(f_a, I2F(512 + weight) / 512));
  159. w1 = F2I(f_h + 512 * cubic_conv(f_a, I2F(0 + weight) / 512));
  160. w2 = F2I(f_h + 512 * cubic_conv(f_a, I2F(512 - weight) / 512));
  161. w3 = F2I(f_h + 512 * cubic_conv(f_a, I2F(1024 - weight) / 512));
  162. w0 = clamp_val(w0, -1024, 1023);
  163. w1 = clamp_val(w1, -1024, 1023);
  164. w2 = clamp_val(w2, -1024, 1023);
  165. w3 = clamp_val(w3, -1024, 1023);
  166. }
  167. val = ((w1 & JZ4760_IPU_RSZ_COEF_MASK) << JZ4760_IPU_RSZ_COEF31_LSB) |
  168. ((w0 & JZ4760_IPU_RSZ_COEF_MASK) << JZ4760_IPU_RSZ_COEF20_LSB);
  169. regmap_write(ipu->map, reg, val);
  170. val = ((w3 & JZ4760_IPU_RSZ_COEF_MASK) << JZ4760_IPU_RSZ_COEF31_LSB) |
  171. ((w2 & JZ4760_IPU_RSZ_COEF_MASK) << JZ4760_IPU_RSZ_COEF20_LSB) |
  172. ((offset & JZ4760_IPU_RSZ_OFFSET_MASK) << JZ4760_IPU_RSZ_OFFSET_LSB);
  173. regmap_write(ipu->map, reg, val);
  174. }
  175. static void jz4725b_set_coefs(struct ingenic_ipu *ipu, unsigned int reg,
  176. unsigned int sharpness, bool downscale,
  177. unsigned int weight, unsigned int offset)
  178. {
  179. u32 val = JZ4725B_IPU_RSZ_LUT_OUT_EN;
  180. unsigned int i;
  181. weight = clamp_val(weight, 0, 512);
  182. if (sharpness == 0)
  183. weight = weight >= 256 ? 512 : 0;
  184. val |= (weight & JZ4725B_IPU_RSZ_LUT_COEF_MASK) << JZ4725B_IPU_RSZ_LUT_COEF_LSB;
  185. if (downscale || !!offset)
  186. val |= JZ4725B_IPU_RSZ_LUT_IN_EN;
  187. regmap_write(ipu->map, reg, val);
  188. if (downscale) {
  189. for (i = 1; i < offset; i++)
  190. regmap_write(ipu->map, reg, JZ4725B_IPU_RSZ_LUT_IN_EN);
  191. }
  192. }
  193. static void ingenic_ipu_set_downscale_coefs(struct ingenic_ipu *ipu,
  194. unsigned int reg,
  195. unsigned int num,
  196. unsigned int denom)
  197. {
  198. unsigned int i, offset, weight, weight_num = denom;
  199. for (i = 0; i < num; i++) {
  200. weight_num = num + (weight_num - num) % (num * 2);
  201. weight = 512 - 512 * (weight_num - num) / (num * 2);
  202. weight_num += denom * 2;
  203. offset = (weight_num - num) / (num * 2);
  204. ipu->soc_info->set_coefs(ipu, reg, ipu->sharpness,
  205. true, weight, offset);
  206. }
  207. }
  208. static void ingenic_ipu_set_integer_upscale_coefs(struct ingenic_ipu *ipu,
  209. unsigned int reg,
  210. unsigned int num)
  211. {
  212. /*
  213. * Force nearest-neighbor scaling and use simple math when upscaling
  214. * by an integer ratio. It looks better, and fixes a few problem cases.
  215. */
  216. unsigned int i;
  217. for (i = 0; i < num; i++)
  218. ipu->soc_info->set_coefs(ipu, reg, 0, false, 512, i == num - 1);
  219. }
  220. static void ingenic_ipu_set_upscale_coefs(struct ingenic_ipu *ipu,
  221. unsigned int reg,
  222. unsigned int num,
  223. unsigned int denom)
  224. {
  225. unsigned int i, offset, weight, weight_num = 0;
  226. for (i = 0; i < num; i++) {
  227. weight = 512 - 512 * weight_num / num;
  228. weight_num += denom;
  229. offset = weight_num >= num;
  230. if (offset)
  231. weight_num -= num;
  232. ipu->soc_info->set_coefs(ipu, reg, ipu->sharpness,
  233. false, weight, offset);
  234. }
  235. }
  236. static void ingenic_ipu_set_coefs(struct ingenic_ipu *ipu, unsigned int reg,
  237. unsigned int num, unsigned int denom)
  238. {
  239. /* Begin programming the LUT */
  240. regmap_write(ipu->map, reg, -1);
  241. if (denom > num)
  242. ingenic_ipu_set_downscale_coefs(ipu, reg, num, denom);
  243. else if (denom == 1)
  244. ingenic_ipu_set_integer_upscale_coefs(ipu, reg, num);
  245. else
  246. ingenic_ipu_set_upscale_coefs(ipu, reg, num, denom);
  247. }
  248. static int reduce_fraction(unsigned int *num, unsigned int *denom)
  249. {
  250. unsigned long d = gcd(*num, *denom);
  251. /* The scaling table has only 31 entries */
  252. if (*num > 31 * d)
  253. return -EINVAL;
  254. *num /= d;
  255. *denom /= d;
  256. return 0;
  257. }
  258. static inline bool osd_changed(struct drm_plane_state *state,
  259. struct drm_plane_state *oldstate)
  260. {
  261. return state->src_x != oldstate->src_x ||
  262. state->src_y != oldstate->src_y ||
  263. state->src_w != oldstate->src_w ||
  264. state->src_h != oldstate->src_h ||
  265. state->crtc_x != oldstate->crtc_x ||
  266. state->crtc_y != oldstate->crtc_y ||
  267. state->crtc_w != oldstate->crtc_w ||
  268. state->crtc_h != oldstate->crtc_h;
  269. }
  270. static void ingenic_ipu_plane_atomic_update(struct drm_plane *plane,
  271. struct drm_atomic_state *state)
  272. {
  273. struct ingenic_ipu *ipu = plane_to_ingenic_ipu(plane);
  274. struct drm_plane_state *newstate = drm_atomic_get_new_plane_state(state, plane);
  275. struct drm_plane_state *oldstate = drm_atomic_get_old_plane_state(state, plane);
  276. const struct drm_format_info *finfo;
  277. u32 ctrl, stride = 0, coef_index = 0, format = 0;
  278. bool needs_modeset, upscaling_w, upscaling_h;
  279. struct ingenic_ipu_private_state *ipu_state;
  280. int err;
  281. if (!newstate || !newstate->fb)
  282. return;
  283. ipu_state = ingenic_ipu_get_new_priv_state(ipu, state);
  284. if (WARN_ON(!ipu_state))
  285. return;
  286. finfo = drm_format_info(newstate->fb->format->format);
  287. if (!ipu->clk_enabled) {
  288. err = clk_enable(ipu->clk);
  289. if (err) {
  290. dev_err(ipu->dev, "Unable to enable clock: %d\n", err);
  291. return;
  292. }
  293. ipu->clk_enabled = true;
  294. }
  295. /* Reset all the registers if needed */
  296. needs_modeset = drm_atomic_crtc_needs_modeset(newstate->crtc->state);
  297. if (needs_modeset) {
  298. regmap_set_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_RST);
  299. /* Enable the chip */
  300. regmap_set_bits(ipu->map, JZ_REG_IPU_CTRL,
  301. JZ_IPU_CTRL_CHIP_EN | JZ_IPU_CTRL_LCDC_SEL);
  302. }
  303. if (ingenic_drm_map_noncoherent(ipu->master))
  304. drm_fb_dma_sync_non_coherent(ipu->drm, oldstate, newstate);
  305. /* New addresses will be committed in vblank handler... */
  306. ipu->addr_y = drm_fb_dma_get_gem_addr(newstate->fb, newstate, 0);
  307. if (finfo->num_planes > 1)
  308. ipu->addr_u = drm_fb_dma_get_gem_addr(newstate->fb, newstate,
  309. 1);
  310. if (finfo->num_planes > 2)
  311. ipu->addr_v = drm_fb_dma_get_gem_addr(newstate->fb, newstate,
  312. 2);
  313. if (!needs_modeset)
  314. return;
  315. /* Or right here if we're doing a full modeset. */
  316. regmap_write(ipu->map, JZ_REG_IPU_Y_ADDR, ipu->addr_y);
  317. regmap_write(ipu->map, JZ_REG_IPU_U_ADDR, ipu->addr_u);
  318. regmap_write(ipu->map, JZ_REG_IPU_V_ADDR, ipu->addr_v);
  319. if (finfo->num_planes == 1)
  320. regmap_set_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_SPKG_SEL);
  321. ingenic_drm_plane_config(ipu->master, plane, DRM_FORMAT_XRGB8888);
  322. /* Set the input height/width/strides */
  323. if (finfo->num_planes > 2)
  324. stride = ((newstate->src_w >> 16) * finfo->cpp[2] / finfo->hsub)
  325. << JZ_IPU_UV_STRIDE_V_LSB;
  326. if (finfo->num_planes > 1)
  327. stride |= ((newstate->src_w >> 16) * finfo->cpp[1] / finfo->hsub)
  328. << JZ_IPU_UV_STRIDE_U_LSB;
  329. regmap_write(ipu->map, JZ_REG_IPU_UV_STRIDE, stride);
  330. stride = ((newstate->src_w >> 16) * finfo->cpp[0]) << JZ_IPU_Y_STRIDE_Y_LSB;
  331. regmap_write(ipu->map, JZ_REG_IPU_Y_STRIDE, stride);
  332. regmap_write(ipu->map, JZ_REG_IPU_IN_GS,
  333. (stride << JZ_IPU_IN_GS_W_LSB) |
  334. ((newstate->src_h >> 16) << JZ_IPU_IN_GS_H_LSB));
  335. switch (finfo->format) {
  336. case DRM_FORMAT_XRGB1555:
  337. format = JZ_IPU_D_FMT_IN_FMT_RGB555 |
  338. JZ_IPU_D_FMT_RGB_OUT_OFT_RGB;
  339. break;
  340. case DRM_FORMAT_XBGR1555:
  341. format = JZ_IPU_D_FMT_IN_FMT_RGB555 |
  342. JZ_IPU_D_FMT_RGB_OUT_OFT_BGR;
  343. break;
  344. case DRM_FORMAT_RGB565:
  345. format = JZ_IPU_D_FMT_IN_FMT_RGB565 |
  346. JZ_IPU_D_FMT_RGB_OUT_OFT_RGB;
  347. break;
  348. case DRM_FORMAT_BGR565:
  349. format = JZ_IPU_D_FMT_IN_FMT_RGB565 |
  350. JZ_IPU_D_FMT_RGB_OUT_OFT_BGR;
  351. break;
  352. case DRM_FORMAT_XRGB8888:
  353. case DRM_FORMAT_XYUV8888:
  354. format = JZ_IPU_D_FMT_IN_FMT_RGB888 |
  355. JZ_IPU_D_FMT_RGB_OUT_OFT_RGB;
  356. break;
  357. case DRM_FORMAT_XBGR8888:
  358. format = JZ_IPU_D_FMT_IN_FMT_RGB888 |
  359. JZ_IPU_D_FMT_RGB_OUT_OFT_BGR;
  360. break;
  361. case DRM_FORMAT_YUYV:
  362. format = JZ_IPU_D_FMT_IN_FMT_YUV422 |
  363. JZ_IPU_D_FMT_YUV_VY1UY0;
  364. break;
  365. case DRM_FORMAT_YVYU:
  366. format = JZ_IPU_D_FMT_IN_FMT_YUV422 |
  367. JZ_IPU_D_FMT_YUV_UY1VY0;
  368. break;
  369. case DRM_FORMAT_UYVY:
  370. format = JZ_IPU_D_FMT_IN_FMT_YUV422 |
  371. JZ_IPU_D_FMT_YUV_Y1VY0U;
  372. break;
  373. case DRM_FORMAT_VYUY:
  374. format = JZ_IPU_D_FMT_IN_FMT_YUV422 |
  375. JZ_IPU_D_FMT_YUV_Y1UY0V;
  376. break;
  377. case DRM_FORMAT_YUV411:
  378. format = JZ_IPU_D_FMT_IN_FMT_YUV411;
  379. break;
  380. case DRM_FORMAT_YUV420:
  381. format = JZ_IPU_D_FMT_IN_FMT_YUV420;
  382. break;
  383. case DRM_FORMAT_YUV422:
  384. format = JZ_IPU_D_FMT_IN_FMT_YUV422;
  385. break;
  386. case DRM_FORMAT_YUV444:
  387. format = JZ_IPU_D_FMT_IN_FMT_YUV444;
  388. break;
  389. default:
  390. WARN_ONCE(1, "Unsupported format");
  391. break;
  392. }
  393. /* Fix output to RGB888 */
  394. format |= JZ_IPU_D_FMT_OUT_FMT_RGB888;
  395. /* Set pixel format */
  396. regmap_write(ipu->map, JZ_REG_IPU_D_FMT, format);
  397. /* Set the output height/width/stride */
  398. regmap_write(ipu->map, JZ_REG_IPU_OUT_GS,
  399. ((newstate->crtc_w * 4) << JZ_IPU_OUT_GS_W_LSB)
  400. | newstate->crtc_h << JZ_IPU_OUT_GS_H_LSB);
  401. regmap_write(ipu->map, JZ_REG_IPU_OUT_STRIDE, newstate->crtc_w * 4);
  402. if (finfo->is_yuv) {
  403. regmap_set_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_CSC_EN);
  404. /*
  405. * Offsets for Chroma/Luma.
  406. * y = source Y - LUMA,
  407. * u = source Cb - CHROMA,
  408. * v = source Cr - CHROMA
  409. */
  410. regmap_write(ipu->map, JZ_REG_IPU_CSC_OFFSET,
  411. 128 << JZ_IPU_CSC_OFFSET_CHROMA_LSB |
  412. 0 << JZ_IPU_CSC_OFFSET_LUMA_LSB);
  413. /*
  414. * YUV422 to RGB conversion table.
  415. * R = C0 / 0x400 * y + C1 / 0x400 * v
  416. * G = C0 / 0x400 * y - C2 / 0x400 * u - C3 / 0x400 * v
  417. * B = C0 / 0x400 * y + C4 / 0x400 * u
  418. */
  419. regmap_write(ipu->map, JZ_REG_IPU_CSC_C0_COEF, 0x4a8);
  420. regmap_write(ipu->map, JZ_REG_IPU_CSC_C1_COEF, 0x662);
  421. regmap_write(ipu->map, JZ_REG_IPU_CSC_C2_COEF, 0x191);
  422. regmap_write(ipu->map, JZ_REG_IPU_CSC_C3_COEF, 0x341);
  423. regmap_write(ipu->map, JZ_REG_IPU_CSC_C4_COEF, 0x811);
  424. }
  425. ctrl = 0;
  426. /*
  427. * Must set ZOOM_SEL before programming bicubic LUTs.
  428. * If the IPU supports bicubic, we enable it unconditionally, since it
  429. * can do anything bilinear can and more.
  430. */
  431. if (ipu->soc_info->has_bicubic)
  432. ctrl |= JZ_IPU_CTRL_ZOOM_SEL;
  433. upscaling_w = ipu_state->num_w > ipu_state->denom_w;
  434. if (upscaling_w)
  435. ctrl |= JZ_IPU_CTRL_HSCALE;
  436. if (ipu_state->num_w != 1 || ipu_state->denom_w != 1) {
  437. if (!ipu->soc_info->has_bicubic && !upscaling_w)
  438. coef_index |= (ipu_state->denom_w - 1) << 16;
  439. else
  440. coef_index |= (ipu_state->num_w - 1) << 16;
  441. ctrl |= JZ_IPU_CTRL_HRSZ_EN;
  442. }
  443. upscaling_h = ipu_state->num_h > ipu_state->denom_h;
  444. if (upscaling_h)
  445. ctrl |= JZ_IPU_CTRL_VSCALE;
  446. if (ipu_state->num_h != 1 || ipu_state->denom_h != 1) {
  447. if (!ipu->soc_info->has_bicubic && !upscaling_h)
  448. coef_index |= ipu_state->denom_h - 1;
  449. else
  450. coef_index |= ipu_state->num_h - 1;
  451. ctrl |= JZ_IPU_CTRL_VRSZ_EN;
  452. }
  453. regmap_update_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_ZOOM_SEL |
  454. JZ_IPU_CTRL_HRSZ_EN | JZ_IPU_CTRL_VRSZ_EN |
  455. JZ_IPU_CTRL_HSCALE | JZ_IPU_CTRL_VSCALE, ctrl);
  456. /* Set the LUT index register */
  457. regmap_write(ipu->map, JZ_REG_IPU_RSZ_COEF_INDEX, coef_index);
  458. if (ipu_state->num_w != 1 || ipu_state->denom_w != 1)
  459. ingenic_ipu_set_coefs(ipu, JZ_REG_IPU_HRSZ_COEF_LUT,
  460. ipu_state->num_w, ipu_state->denom_w);
  461. if (ipu_state->num_h != 1 || ipu_state->denom_h != 1)
  462. ingenic_ipu_set_coefs(ipu, JZ_REG_IPU_VRSZ_COEF_LUT,
  463. ipu_state->num_h, ipu_state->denom_h);
  464. /* Clear STATUS register */
  465. regmap_write(ipu->map, JZ_REG_IPU_STATUS, 0);
  466. /* Start IPU */
  467. regmap_set_bits(ipu->map, JZ_REG_IPU_CTRL,
  468. JZ_IPU_CTRL_RUN | JZ_IPU_CTRL_FM_IRQ_EN);
  469. dev_dbg(ipu->dev, "Scaling %ux%u to %ux%u (%u:%u horiz, %u:%u vert)\n",
  470. newstate->src_w >> 16, newstate->src_h >> 16,
  471. newstate->crtc_w, newstate->crtc_h,
  472. ipu_state->num_w, ipu_state->denom_w,
  473. ipu_state->num_h, ipu_state->denom_h);
  474. }
  475. static int ingenic_ipu_plane_atomic_check(struct drm_plane *plane,
  476. struct drm_atomic_state *state)
  477. {
  478. struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state,
  479. plane);
  480. struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
  481. plane);
  482. unsigned int num_w, denom_w, num_h, denom_h, xres, yres, max_w, max_h;
  483. struct ingenic_ipu *ipu = plane_to_ingenic_ipu(plane);
  484. struct drm_crtc *crtc = new_plane_state->crtc ?: old_plane_state->crtc;
  485. struct drm_crtc_state *crtc_state;
  486. struct ingenic_ipu_private_state *ipu_state;
  487. if (!crtc)
  488. return 0;
  489. crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
  490. if (WARN_ON(!crtc_state))
  491. return -EINVAL;
  492. ipu_state = ingenic_ipu_get_priv_state(ipu, state);
  493. if (IS_ERR(ipu_state))
  494. return PTR_ERR(ipu_state);
  495. /* Request a full modeset if we are enabling or disabling the IPU. */
  496. if (!old_plane_state->crtc ^ !new_plane_state->crtc)
  497. crtc_state->mode_changed = true;
  498. if (!new_plane_state->crtc ||
  499. !crtc_state->mode.hdisplay || !crtc_state->mode.vdisplay)
  500. goto out_check_damage;
  501. /* Plane must be fully visible */
  502. if (new_plane_state->crtc_x < 0 || new_plane_state->crtc_y < 0 ||
  503. new_plane_state->crtc_x + new_plane_state->crtc_w > crtc_state->mode.hdisplay ||
  504. new_plane_state->crtc_y + new_plane_state->crtc_h > crtc_state->mode.vdisplay)
  505. return -EINVAL;
  506. /* Minimum size is 4x4 */
  507. if ((new_plane_state->src_w >> 16) < 4 || (new_plane_state->src_h >> 16) < 4)
  508. return -EINVAL;
  509. /* Input and output lines must have an even number of pixels. */
  510. if (((new_plane_state->src_w >> 16) & 1) || (new_plane_state->crtc_w & 1))
  511. return -EINVAL;
  512. if (!osd_changed(new_plane_state, old_plane_state))
  513. goto out_check_damage;
  514. crtc_state->mode_changed = true;
  515. xres = new_plane_state->src_w >> 16;
  516. yres = new_plane_state->src_h >> 16;
  517. /*
  518. * Increase the scaled image's theorical width/height until we find a
  519. * configuration that has valid scaling coefficients, up to 102% of the
  520. * screen's resolution. This makes sure that we can scale from almost
  521. * every resolution possible at the cost of a very small distorsion.
  522. * The CRTC_W / CRTC_H are not modified.
  523. */
  524. max_w = crtc_state->mode.hdisplay * 102 / 100;
  525. max_h = crtc_state->mode.vdisplay * 102 / 100;
  526. for (denom_w = xres, num_w = new_plane_state->crtc_w; num_w <= max_w; num_w++)
  527. if (!reduce_fraction(&num_w, &denom_w))
  528. break;
  529. if (num_w > max_w)
  530. return -EINVAL;
  531. for (denom_h = yres, num_h = new_plane_state->crtc_h; num_h <= max_h; num_h++)
  532. if (!reduce_fraction(&num_h, &denom_h))
  533. break;
  534. if (num_h > max_h)
  535. return -EINVAL;
  536. ipu_state->num_w = num_w;
  537. ipu_state->num_h = num_h;
  538. ipu_state->denom_w = denom_w;
  539. ipu_state->denom_h = denom_h;
  540. out_check_damage:
  541. if (ingenic_drm_map_noncoherent(ipu->master))
  542. drm_atomic_helper_check_plane_damage(state, new_plane_state);
  543. return 0;
  544. }
  545. static void ingenic_ipu_plane_atomic_disable(struct drm_plane *plane,
  546. struct drm_atomic_state *state)
  547. {
  548. struct ingenic_ipu *ipu = plane_to_ingenic_ipu(plane);
  549. regmap_set_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_STOP);
  550. regmap_clear_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_CHIP_EN);
  551. ingenic_drm_plane_disable(ipu->master, plane);
  552. if (ipu->clk_enabled) {
  553. clk_disable(ipu->clk);
  554. ipu->clk_enabled = false;
  555. }
  556. }
  557. static const struct drm_plane_helper_funcs ingenic_ipu_plane_helper_funcs = {
  558. .atomic_update = ingenic_ipu_plane_atomic_update,
  559. .atomic_check = ingenic_ipu_plane_atomic_check,
  560. .atomic_disable = ingenic_ipu_plane_atomic_disable,
  561. };
  562. static int
  563. ingenic_ipu_plane_atomic_get_property(struct drm_plane *plane,
  564. const struct drm_plane_state *state,
  565. struct drm_property *property, u64 *val)
  566. {
  567. struct ingenic_ipu *ipu = plane_to_ingenic_ipu(plane);
  568. if (property != ipu->sharpness_prop)
  569. return -EINVAL;
  570. *val = ipu->sharpness;
  571. return 0;
  572. }
  573. static int
  574. ingenic_ipu_plane_atomic_set_property(struct drm_plane *plane,
  575. struct drm_plane_state *state,
  576. struct drm_property *property, u64 val)
  577. {
  578. struct ingenic_ipu *ipu = plane_to_ingenic_ipu(plane);
  579. struct drm_crtc_state *crtc_state;
  580. bool mode_changed;
  581. if (property != ipu->sharpness_prop)
  582. return -EINVAL;
  583. mode_changed = val != ipu->sharpness;
  584. ipu->sharpness = val;
  585. if (state->crtc) {
  586. crtc_state = drm_atomic_get_existing_crtc_state(state->state, state->crtc);
  587. if (WARN_ON(!crtc_state))
  588. return -EINVAL;
  589. crtc_state->mode_changed |= mode_changed;
  590. }
  591. return 0;
  592. }
  593. static const struct drm_plane_funcs ingenic_ipu_plane_funcs = {
  594. .update_plane = drm_atomic_helper_update_plane,
  595. .disable_plane = drm_atomic_helper_disable_plane,
  596. .reset = drm_atomic_helper_plane_reset,
  597. .destroy = drm_plane_cleanup,
  598. .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
  599. .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
  600. .atomic_get_property = ingenic_ipu_plane_atomic_get_property,
  601. .atomic_set_property = ingenic_ipu_plane_atomic_set_property,
  602. };
  603. static struct drm_private_state *
  604. ingenic_ipu_duplicate_state(struct drm_private_obj *obj)
  605. {
  606. struct ingenic_ipu_private_state *state = to_ingenic_ipu_priv_state(obj->state);
  607. state = kmemdup(state, sizeof(*state), GFP_KERNEL);
  608. if (!state)
  609. return NULL;
  610. __drm_atomic_helper_private_obj_duplicate_state(obj, &state->base);
  611. return &state->base;
  612. }
  613. static void ingenic_ipu_destroy_state(struct drm_private_obj *obj,
  614. struct drm_private_state *state)
  615. {
  616. struct ingenic_ipu_private_state *priv_state = to_ingenic_ipu_priv_state(state);
  617. kfree(priv_state);
  618. }
  619. static const struct drm_private_state_funcs ingenic_ipu_private_state_funcs = {
  620. .atomic_duplicate_state = ingenic_ipu_duplicate_state,
  621. .atomic_destroy_state = ingenic_ipu_destroy_state,
  622. };
  623. static irqreturn_t ingenic_ipu_irq_handler(int irq, void *arg)
  624. {
  625. struct ingenic_ipu *ipu = arg;
  626. struct drm_crtc *crtc = drm_crtc_from_index(ipu->drm, 0);
  627. unsigned int dummy;
  628. /* dummy read allows CPU to reconfigure IPU */
  629. if (ipu->soc_info->manual_restart)
  630. regmap_read(ipu->map, JZ_REG_IPU_STATUS, &dummy);
  631. /* ACK interrupt */
  632. regmap_write(ipu->map, JZ_REG_IPU_STATUS, 0);
  633. /* Set previously cached addresses */
  634. regmap_write(ipu->map, JZ_REG_IPU_Y_ADDR, ipu->addr_y);
  635. regmap_write(ipu->map, JZ_REG_IPU_U_ADDR, ipu->addr_u);
  636. regmap_write(ipu->map, JZ_REG_IPU_V_ADDR, ipu->addr_v);
  637. /* Run IPU for the new frame */
  638. if (ipu->soc_info->manual_restart)
  639. regmap_set_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_RUN);
  640. drm_crtc_handle_vblank(crtc);
  641. return IRQ_HANDLED;
  642. }
  643. static const struct regmap_config ingenic_ipu_regmap_config = {
  644. .reg_bits = 32,
  645. .val_bits = 32,
  646. .reg_stride = 4,
  647. .max_register = JZ_REG_IPU_OUT_PHY_T_ADDR,
  648. };
  649. static int ingenic_ipu_bind(struct device *dev, struct device *master, void *d)
  650. {
  651. struct platform_device *pdev = to_platform_device(dev);
  652. struct ingenic_ipu_private_state *private_state;
  653. const struct soc_info *soc_info;
  654. struct drm_device *drm = d;
  655. struct drm_plane *plane;
  656. struct ingenic_ipu *ipu;
  657. void __iomem *base;
  658. unsigned int sharpness_max;
  659. int err, irq;
  660. ipu = devm_kzalloc(dev, sizeof(*ipu), GFP_KERNEL);
  661. if (!ipu)
  662. return -ENOMEM;
  663. soc_info = of_device_get_match_data(dev);
  664. if (!soc_info) {
  665. dev_err(dev, "Missing platform data\n");
  666. return -EINVAL;
  667. }
  668. ipu->dev = dev;
  669. ipu->drm = drm;
  670. ipu->master = master;
  671. ipu->soc_info = soc_info;
  672. base = devm_platform_ioremap_resource(pdev, 0);
  673. if (IS_ERR(base)) {
  674. dev_err(dev, "Failed to get memory resource\n");
  675. return PTR_ERR(base);
  676. }
  677. ipu->map = devm_regmap_init_mmio(dev, base, &ingenic_ipu_regmap_config);
  678. if (IS_ERR(ipu->map)) {
  679. dev_err(dev, "Failed to create regmap\n");
  680. return PTR_ERR(ipu->map);
  681. }
  682. irq = platform_get_irq(pdev, 0);
  683. if (irq < 0)
  684. return irq;
  685. ipu->clk = devm_clk_get(dev, "ipu");
  686. if (IS_ERR(ipu->clk)) {
  687. dev_err(dev, "Failed to get pixel clock\n");
  688. return PTR_ERR(ipu->clk);
  689. }
  690. err = devm_request_irq(dev, irq, ingenic_ipu_irq_handler, 0,
  691. dev_name(dev), ipu);
  692. if (err) {
  693. dev_err(dev, "Unable to request IRQ\n");
  694. return err;
  695. }
  696. plane = &ipu->plane;
  697. dev_set_drvdata(dev, plane);
  698. drm_plane_helper_add(plane, &ingenic_ipu_plane_helper_funcs);
  699. err = drm_universal_plane_init(drm, plane, 1, &ingenic_ipu_plane_funcs,
  700. soc_info->formats, soc_info->num_formats,
  701. NULL, DRM_PLANE_TYPE_OVERLAY, NULL);
  702. if (err) {
  703. dev_err(dev, "Failed to init plane: %i\n", err);
  704. return err;
  705. }
  706. if (ingenic_drm_map_noncoherent(master))
  707. drm_plane_enable_fb_damage_clips(plane);
  708. /*
  709. * Sharpness settings range is [0,32]
  710. * 0 : nearest-neighbor
  711. * 1 : bilinear
  712. * 2 .. 32 : bicubic (translated to sharpness factor -0.25 .. -4.0)
  713. */
  714. sharpness_max = soc_info->has_bicubic ? 32 : 1;
  715. ipu->sharpness_prop = drm_property_create_range(drm, 0, "sharpness",
  716. 0, sharpness_max);
  717. if (!ipu->sharpness_prop) {
  718. dev_err(dev, "Unable to create sharpness property\n");
  719. return -ENOMEM;
  720. }
  721. /* Default sharpness factor: -0.125 * 8 = -1.0 */
  722. ipu->sharpness = soc_info->has_bicubic ? 8 : 1;
  723. drm_object_attach_property(&plane->base, ipu->sharpness_prop,
  724. ipu->sharpness);
  725. err = clk_prepare(ipu->clk);
  726. if (err) {
  727. dev_err(dev, "Unable to prepare clock\n");
  728. return err;
  729. }
  730. private_state = kzalloc(sizeof(*private_state), GFP_KERNEL);
  731. if (!private_state) {
  732. err = -ENOMEM;
  733. goto err_clk_unprepare;
  734. }
  735. drm_atomic_private_obj_init(drm, &ipu->private_obj, &private_state->base,
  736. &ingenic_ipu_private_state_funcs);
  737. return 0;
  738. err_clk_unprepare:
  739. clk_unprepare(ipu->clk);
  740. return err;
  741. }
  742. static void ingenic_ipu_unbind(struct device *dev,
  743. struct device *master, void *d)
  744. {
  745. struct ingenic_ipu *ipu = dev_get_drvdata(dev);
  746. drm_atomic_private_obj_fini(&ipu->private_obj);
  747. clk_unprepare(ipu->clk);
  748. }
  749. static const struct component_ops ingenic_ipu_ops = {
  750. .bind = ingenic_ipu_bind,
  751. .unbind = ingenic_ipu_unbind,
  752. };
  753. static int ingenic_ipu_probe(struct platform_device *pdev)
  754. {
  755. return component_add(&pdev->dev, &ingenic_ipu_ops);
  756. }
  757. static int ingenic_ipu_remove(struct platform_device *pdev)
  758. {
  759. component_del(&pdev->dev, &ingenic_ipu_ops);
  760. return 0;
  761. }
  762. static const u32 jz4725b_ipu_formats[] = {
  763. /*
  764. * While officially supported, packed YUV 4:2:2 formats can cause
  765. * random hardware crashes on JZ4725B under certain circumstances.
  766. * It seems to happen with some specific resize ratios.
  767. * Until a proper workaround or fix is found, disable these formats.
  768. DRM_FORMAT_YUYV,
  769. DRM_FORMAT_YVYU,
  770. DRM_FORMAT_UYVY,
  771. DRM_FORMAT_VYUY,
  772. */
  773. DRM_FORMAT_YUV411,
  774. DRM_FORMAT_YUV420,
  775. DRM_FORMAT_YUV422,
  776. DRM_FORMAT_YUV444,
  777. };
  778. static const struct soc_info jz4725b_soc_info = {
  779. .formats = jz4725b_ipu_formats,
  780. .num_formats = ARRAY_SIZE(jz4725b_ipu_formats),
  781. .has_bicubic = false,
  782. .manual_restart = true,
  783. .set_coefs = jz4725b_set_coefs,
  784. };
  785. static const u32 jz4760_ipu_formats[] = {
  786. DRM_FORMAT_XRGB1555,
  787. DRM_FORMAT_XBGR1555,
  788. DRM_FORMAT_RGB565,
  789. DRM_FORMAT_BGR565,
  790. DRM_FORMAT_XRGB8888,
  791. DRM_FORMAT_XBGR8888,
  792. DRM_FORMAT_YUYV,
  793. DRM_FORMAT_YVYU,
  794. DRM_FORMAT_UYVY,
  795. DRM_FORMAT_VYUY,
  796. DRM_FORMAT_YUV411,
  797. DRM_FORMAT_YUV420,
  798. DRM_FORMAT_YUV422,
  799. DRM_FORMAT_YUV444,
  800. DRM_FORMAT_XYUV8888,
  801. };
  802. static const struct soc_info jz4760_soc_info = {
  803. .formats = jz4760_ipu_formats,
  804. .num_formats = ARRAY_SIZE(jz4760_ipu_formats),
  805. .has_bicubic = true,
  806. .manual_restart = false,
  807. .set_coefs = jz4760_set_coefs,
  808. };
  809. static const struct of_device_id ingenic_ipu_of_match[] = {
  810. { .compatible = "ingenic,jz4725b-ipu", .data = &jz4725b_soc_info },
  811. { .compatible = "ingenic,jz4760-ipu", .data = &jz4760_soc_info },
  812. { /* sentinel */ },
  813. };
  814. MODULE_DEVICE_TABLE(of, ingenic_ipu_of_match);
  815. static struct platform_driver ingenic_ipu_driver = {
  816. .driver = {
  817. .name = "ingenic-ipu",
  818. .of_match_table = ingenic_ipu_of_match,
  819. },
  820. .probe = ingenic_ipu_probe,
  821. .remove = ingenic_ipu_remove,
  822. };
  823. struct platform_driver *ingenic_ipu_driver_ptr = &ingenic_ipu_driver;