{"id":451,"date":"2024-09-11T14:50:38","date_gmt":"2024-09-11T06:50:38","guid":{"rendered":"https:\/\/www.yanwenkai.com\/?p=451"},"modified":"2025-03-12T22:09:51","modified_gmt":"2025-03-12T14:09:51","slug":"lcd%e8%a7%a6%e6%8e%a7%e5%b1%8f%e7%a7%bb%e6%a4%8d%e4%b9%8blcd%e9%a9%b1%e5%8a%a8%e7%af%87","status":"publish","type":"post","link":"https:\/\/www.yanwenkai.com\/?p=451","title":{"rendered":"LCD\u89e6\u63a7\u5c4f\u79fb\u690d\u4e4bLCD\u663e\u793a\u9a71\u52a8\u7bc7"},"content":{"rendered":"<h1>1 \u524d\u8a00<\/h1>\n<p>&emsp;&emsp;\u5173\u4e8eframebuffer\u7684\u79fb\u690d\uff0c\u8fd9\u662f\u6211\u7b2c\u4e00\u6b21\u63a5\u89e6\u7a0d\u5fae\u9ad8\u7aef\u70b9\u7684\u9a71\u52a8\u3002\u4ee5\u524d\u90fd\u662f\u5b66\u4e60\u4f7f\u7528spi\u548ci2c\u9a71\u52a8\uff0c\u8fd9\u4e9b\u9a71\u52a8\u7684\u6574\u4f53\u6846\u67b6\u5df2\u7ecf\u5f88\u719f\u6089\u4e86\uff0c\u4e14\u7f51\u4e0a\u8d44\u6599\u90fd\u70c2\u5927\u8857\u4e86\uff0c\u6240\u4ee5\u9047\u5230\u95ee\u9898\u57fa\u672c\u90fd\u80fd\u8f7b\u677e\u89e3\u51b3\u3002\u5230\u4e86\u8fd9\u4e2a\u9a71\u52a8\uff0c\u5c31\u9047\u5230\u4e86\u5f88\u591a\u65b0\u95ee\u9898\uff1b\u9996\u5148\u8fd9\u4e2a\u6846\u67b6\u751a\u81f3\u90fd\u662f\u6211\u7b2c\u4e00\u6b21\u542c\u8bf4\uff0c\u56e0\u6b64\u5b98\u65b9\u9ad8\u5ea6\u4f18\u5316\u7684\u4ee3\u7801\u8bfb\u8d77\u6765\u5f88\u8d39\u529b\uff01\u4e14\u4e0d\u5f97\u4e0d\u5410\u69fd\u7684\u4e00\u70b9\u662f\u5b98\u65b9\u4e3a\u4e86\u517c\u5bb9\u6027\u8bbe\u7f6e\u4e86\u4e00\u5927\u5806\u5b8f\uff0c\u52a0\u4e0a\u5404\u79cd\u53d8\u91cf\u7684\u91cd\u8d4b\u503c\uff0c\u7efc\u5408\u4e0b\u6765\u4ee3\u7801\u7b80\u76f4\u6ca1\u6cd5\u5f80\u4e0b\u8bfb\u3002<br \/>\n&emsp;&emsp;\u6ca1\u529e\u6cd5\uff0c\u53ea\u80fd\u53bb\u7f51\u4e0a\u67e5\u627eframebuffer\u6846\u67b6\u7684\u8d44\u6599\u5148\u5b66\u4e60\u4e00\u4e0b\uff1b\u5e78\u8fd0\u7684\u662f\u6211\u627e\u5230\u4e86\u62f1\u5352\u5927\u4f6c\u7684framebuffer\u7684\u9a71\u52a8\uff0c\u4e5f\u662f\u57fa\u4e8eili9488\u5199\u7684\uff1b\u6309\u9053\u7406\u8bb2\u7528\u5927\u4f6c\u7684\u9a71\u52a8\uff0c\u53ea\u8981\u9002\u914d\u597d\u8bbe\u5907\u6811\u57fa\u672c\u4e0a\u5c4f\u5e55\u4e5f\u5c31\u80fd\u987a\u5229\u70b9\u4eae\u4e86\u3002\u57fa\u4e8e\u6b64\u6211\u5c31\u6539\u8d77\u4e86\u81ea\u5df1\u7684\u4ee3\u7801\u3002<br \/>\n&emsp;&emsp;\u7136\u540e\u5c31\u5361\u5728\u8bbe\u5907\u6811\u7684\u7b2c\u4e00\u9053\u96be\u5173\u4e0a\u4e86\u3002\u6211\u7ed3\u5408\u62f1\u5352\u5927\u4f6c\u7684\u8bbe\u5907\u6811\u548c\u5b98\u65b9\u57fa\u4e8est7789v\u7684lcd\u5c4f\u5e55\u7684\u8bbe\u5907\u6811\uff0c\u7f16\u5199\u8fed\u4ee3\u4e86\u81ea\u5df1\u591a\u4e2a\u7248\u672c\u7684\u8bbe\u5907\u6811\uff1b\u867d\u8bf4\u7ed3\u5408\u9a71\u52a8\u5b8c\u6210\u4e86\u5c4f\u5e55\u7684\u5404\u9879\u914d\u7f6e\u5de5\u4f5c\uff0c\u4f46\u5c4f\u5e55\u59cb\u7ec8\u5c31\u662f\u70b9\u4e0d\u4eae\u3002\u6000\u7591\u5404\u79cd\u95ee\u9898\uff0c\u5404\u79cd\u5c1d\u8bd5\uff0c\u5c31\u662f\u5931\u8d25\u3002\u540e\u9762\u5b9e\u5728\u662f\u6ca1\u601d\u8def\u4e86\uff0c\u5c31\u60f3\u7740\u5148\u7528\u5b98\u65b9st7789v\u7684\u5c4f\u5e55\u628aframebuffer\u9a71\u52a8\u70b9\u4eae\u5c4f\u5e55\u8fd9\u6761\u8def\u8d70\u901a\uff0c\u52a0\u6df1\u7406\u89e3\u540e\u518d\u56de\u5934\u70b9\u4eae\u81ea\u5df1\u7684\u5c4f\u5e55\u3002<br \/>\n&emsp;&emsp;\u7ed3\u679c\u4e0d\u8bd5\u4e0d\u77e5\u9053\uff0c\u4e00\u8bd5\u624d\u53d1\u73b0\u5b98\u65b9\u793a\u4f8b\u7684\u8bbe\u5907\u6811\u4e5f\u662f\u9519\u8bef\u7684\u3002\u540e\u9762\u987a\u7406\u6210\u7ae0\u5730\u5728\u5b98\u65b9\u8bba\u575b\u4e0a\u63d0\u95ee,\u5b98\u65b9\u7ed9\u51fa\u4e86\u6b63\u786e\u7684\u8bbe\u5907\u6811\u3002\u4ece\u800c\u722c\u51fa\u4e86\u7b2c\u4e00\u4e2a\u6df1\u5751\u3002(\u5b98\u65b9\u8bbe\u5907\u6811\u5728\u539f\u6709\u7684\u57fa\u7840\u4e0a\u5173\u95ed\u4e86\u4e00\u4e2aspi\u7684\u8282\u70b9)<br \/>\n&emsp;&emsp;\u63a5\u7740\u53c8\u8e0f\u8fdb\u7b2c\u4e8c\u4e2a\u6df1\u5751\uff0c\u5185\u6838\u7533\u8bf7\u7684framebuffer\u7a7a\u95f4\u5730\u5740\u600e\u4e48\u90fd\u548c\u5e94\u7528\u5c42\u901a\u4fe1\u4e0d\u6210\u529f\u3002\u4e00\u5f00\u59cb\u6000\u7591\u662f\u81ea\u5df1\u5f97\u95ee\u9898\uff0c\u662f\u81ea\u5df1\u5199\u5f97fb_mmap\u51fd\u6570\u6709\u95ee\u9898\uff0c\u4f46\u767e\u5ea6\u5404\u79cdmmap\u51fd\u6570\u7684\u5199\u6cd5\uff0c\u4ee3\u7801\u9b54\u6539\u65e0\u6570\u6b21\u90fd\u5931\u8d25\u4e86\uff1b\u4e8e\u662f\u6000\u7591\u662f\u5185\u6838\u7248\u672c\u7684\u95ee\u9898\uff0c\u4e13\u95e8\u5b9e\u73b0\u4e86\u64cd\u4f5c\u65b9\u6cd5\u7ed3\u6784\u4f53ops\u4e2d\u7684open\u51fd\u6570\u548cfb_mmap\u51fd\u6570\u505a\u5bf9\u6bd4\uff0c\u770b\u5230\u5e95\u95ee\u9898\u51fa\u5728\u54ea\u91cc\uff0c\u7ed3\u679c\u53d1\u73b0open\u51fd\u6570\u88ab\u6210\u529f\u8c03\u7528\uff0c\u4f46fb_mmap\u51fd\u6570\u4e00\u6837\u5730\u5b9e\u73b0\u65b9\u5f0f\uff0c\u5b83\u5c31\u6ca1\u88ab\u6210\u529f\u8c03\u7528\u3002\u5206\u6790\u6211\u8fd9\u4e2afb_mmap\u51fd\u6570\u7684\u5b9e\u73b0\u65b9\u5f0f\uff0c\u8fd9\u662f\u4ece\u7f51\u4e0a\u591a\u4e2a\u6559\u7a0b\u4ea4\u53c9\u9a8c\u8bc1\u5f97\u5230\u5730\u53ef\u884c\u65b9\u6cd5\u554a\uff01\u5e94\u8be5\u6ca1\u95ee\u9898\uff01\u65e2\u7136\u81ea\u5df1\u7684\u9a71\u52a8\u6709\u95ee\u9898\uff0c\u90a3\u5c31\u770b\u5b98\u65b9\u9a71\u52a8\u7684\u8c03\u7528\u6d41\u7a0b\uff0c\u518d\u53cd\u5411\u5206\u6790\u81ea\u5df1\u7684\u4ee3\u7801\u3002<br \/>\n&emsp;&emsp;\u5206\u6790\u5b98\u65b9st7789v\u7684framebuffer\u9a71\u52a8\uff0c\u5bf9\u6bd4\u5dee\u5f02\u67e5\u627e\u8c03\u7528\u5931\u8d25\u539f\u56e0\u3002\u53d1\u73b0\u5b98\u65b9\u4f7f\u7528\u5f97ops\u4e2d\u4ece\u5934\u5230\u5c3e\u4e5f\u6ca1\u7528\u5230mmap\u51fd\u6570\uff0c\u4f46\u5728\u5e94\u7528\u5c42\u7adf\u7136\u80fd\u83b7\u53d6\u5230\u5730\u5740\uff0c\u8fd9\u662f\u8ba9\u6211\u4ea7\u751f\u4e86\u66f4\u6df1\u7684\u7591\u95ee\uff01\u56e0\u4e3a\u6211\u65e0\u8bba\u662f\u4ece\u767e\u5ea6\u8fd8\u662fcsdn\u7b49\u5404\u79cd\u5730\u65b9\u67e5\u627e\u7684\u8d44\u6599\u90fd\u663e\u793a\uff1a\u60f3\u8981\u5728\u5e94\u7528\u5c42\u8c03\u7528mmap\u7684\u51fd\u6570\uff0c\u90a3\u9a71\u52a8\u5c42\u5fc5\u987b\u8981\u5728\u64cd\u4f5c\u65b9\u6cd5\u7ed3\u6784\u4f53ops\u4e2d\u5b9e\u73b0\u81ea\u5df1\u7684mmap\u51fd\u6570\u3002\u800c\u6211\u5728\u6700\u65b0\u7684\u9a71\u52a8\u6587\u4ef6\u4e2d\u5e76\u672a\u53d1\u73b0\u8fd9\u4e2a\u51fd\u6570\uff0c\u4e14\u4e4b\u524d\u81ea\u5df1\u5199\u7684\u9a71\u52a8\u4e2d\u5b9e\u73b0\u8fc7\u8fd9\u4e2a\u51fd\u6570\uff0c\u4f46\u7cfb\u7edf\u672a\u8c03\u7528\uff08\u5b9e\u73b0\u4e86fb_open\u51fd\u6570\u548cfb_mmap\u51fd\u6570\uff0c\u4f46\u53ea\u8c03\u7528\u4e86open\u51fd\u6570\uff0c\u6000\u7591\u662fmmap\u51fd\u6570\u7684\u53c2\u6570\u4e0d\u5bf9\uff0c\u5bfc\u81f4\u6ca1\u8c03\u7528\uff09\u3002<br \/>\n&emsp;&emsp;\u57fa\u4e8e\u4e0a\u9762\u4e24\u70b9\u63d0\u51fa\u4e86\u6700\u7ec8\u7684\u95ee\u9898\uff0c\u8fd9\u4e2a\u51fd\u6570\u5728\u54ea\uff0c\u600e\u4e48\u88ab\u8c03\u7528\u7684\uff1f\u5c42\u5c42\u5206\u6790\uff0c\u6700\u7ec8\u62bd\u4e1d\u5265\u8327\u627e\u5230\/video\/fbdev\/core\/fbmem.c\u6587\u4ef6\uff0c\u53d1\u73b0\u4e86framebuffer\u5b50\u7cfb\u7edf\u6846\u67b6\u5bf9\u4e8emmap\u51fd\u6570\u7684\u5904\u7406\u8c03\u7528\u903b\u8f91\uff0c\u4e5f\u660e\u767d\u4e86\u8fd9\u4e2a\u5b50\u7cfb\u7edf\u5bf9\u4e8e\u64cd\u4f5c\u65b9\u6cd5\u7ed3\u6784\u4f53\u6709\u81ea\u5df1\u4e00\u5957\u9ed8\u8ba4\u7684\u5b9e\u73b0\u51fd\u6570\uff0c\u5728\u7528\u6237\u9a71\u52a8\u672a\u5b9e\u73b0\u81ea\u5df1\u7684\u64cd\u4f5c\u65b9\u6cd5\u7ed3\u6784\u4f53\u65f6\uff0c\u7cfb\u7edf\u9ed8\u8ba4\u8c03\u7528\u8fd9\u4e2a\u51fd\u6570\u3002<br \/>\n&emsp;&emsp;\u81f3\u6b64\u6574\u4e2a\u9a71\u52a8\u7684\u8c03\u7528\u6d41\u7a0b\u5c31\u5168\u901a\u4e86\uff0c\u7406\u6240\u5e94\u5f53\u5c4f\u5e55\u4e5f\u987a\u5229\u70b9\u4eae\u4e86\u3002<\/p>\n<h1>2 spi\u9a71\u52a8\u7b80\u5355\u5206\u6790<\/h1>\n<p>&emsp;&emsp;\u5b98\u65b9\u7684\u89e6\u6478\u5c4f\u9879\u76ee\u6765\u6e90\u4e8e\u6811\u8393\u6d3e\u7684\u793a\u4f8b\uff0c\u6240\u4ee5\u4ed6\u7684spi\u9a71\u52a8\u4ee3\u7801\u5df2\u7ecf\u5f88\u6210\u719f\u7b80\u6d01\u4e86\uff1b\u6574\u4f53\u601d\u8def\u4e5f\u6bd4\u8f83\u6e05\u6670\u660e\u4e86\uff0c\u7a0d\u5fae\u7528\u5fc3\u770b\u770b\u5c31\u80fd\u770b\u61c2\u3002\u4f46\u7f3a\u70b9\u5c31\u662f\u6211\u4e0a\u4e2a\u9879\u76ee\u63d0\u8fc7\u7684\uff0c\u5168\u90e8\u5f15\u811a\u90fd\u7528\u7684sysfs\u6587\u4ef6\u7cfb\u7edf\u5728\u5e94\u7528\u5c42\u8c03\u7528\uff1b\u8fd9\u6837\u7684\u65b9\u6cd5\u611f\u89c9\u867d\u7136\u7b80\u5355\u63d0\u9ad8\u4e86\u9a71\u52a8\u7684\u517c\u5bb9\u6027\uff0c\u4f46\u4e5f\u589e\u52a0\u4e86\u5e94\u7528\u5c42\u4ee3\u7801\u7684\u590d\u6742\u7a0b\u5ea6\u3002<br \/>\n&emsp;&emsp;\u8fd9\u91cc\u7b80\u5355\u68b3\u7406\u4e00\u4e0b\u4ece\u5e94\u7528\u5c42\u5230\u9a71\u52a8\u7684\u8c03\u7528\u6d41\u7a0b\uff0c\u901a\u8fc7\u7b2c3\u30014\u4e24\u6b65\u53ef\u4ee5\u770b\u51fa\u5f15\u811a\u7684\u72b6\u6001\u63a7\u5236\u662f\u901a\u8fc7sysfs\u6587\u4ef6\u7cfb\u7edf\u5728\u5e94\u7528\u5c42\u63a7\u5236\u7684\uff0c\u800cspi\u4f20\u8f93\u6570\u636e\u5219\u662f\u901a\u8fc7ioctl\u65b9\u5f0f\u5c06\u6570\u636e\u53d1\u9001\u7ed9\u9a71\u52a8\uff0c\u7531\u9a71\u52a8\u5904\u7406\u6570\u636e\u540e\u5c06\u6570\u636e\u901a\u8fc7spi\u5b50\u7cfb\u7edf\u4f20\u8f93\u7ed9\u786c\u4ef6\u7684\uff01<\/p>\n<p><center><br \/>\n<div class='fancybox-wrapper lazyload-container-unload' data-fancybox='post-images' href='https:\/\/www.yanwenkai.com:7777\/images\/2024\/09\/05\/2024-09-03-90959318c4acb6429.png'><img class=\"lazyload lazyload-style-1\" src=\"data:image\/svg+xml;base64,PCEtLUFyZ29uTG9hZGluZy0tPgo8c3ZnIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgc3Ryb2tlPSIjZmZmZmZmMDAiPjxnPjwvZz4KPC9zdmc+\"  loading=\"lazy\" decoding=\"async\" data-original=\"https:\/\/www.yanwenkai.com:7777\/images\/2024\/09\/05\/2024-09-03-90959318c4acb6429.png\" src=\"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB\/AAffA0nNPuCLAAAAAElFTkSuQmCC\" width=\"600\" height=\"400\"><\/div><br \/>\n<\/center><\/p>\n<p>&emsp;&emsp;\u5982\u4e0b\u56fe\u662fsysfs\u6587\u4ef6\u7cfb\u7edf\u4e2d\u8bbe\u5907\u6811\u4e0e\u5e94\u7528\u5c42\u7684\u8c03\u7528\u6d41\u7a0b\u3002<\/p>\n<p><center><br \/>\n<div class='fancybox-wrapper lazyload-container-unload' data-fancybox='post-images' href='https:\/\/www.yanwenkai.com:7777\/images\/2024\/09\/10\/2024-09-03-10.png'><img class=\"lazyload lazyload-style-1\" src=\"data:image\/svg+xml;base64,PCEtLUFyZ29uTG9hZGluZy0tPgo8c3ZnIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgc3Ryb2tlPSIjZmZmZmZmMDAiPjxnPjwvZz4KPC9zdmc+\"  loading=\"lazy\" decoding=\"async\" data-original=\"https:\/\/www.yanwenkai.com:7777\/images\/2024\/09\/10\/2024-09-03-10.png\" src=\"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB\/AAffA0nNPuCLAAAAAElFTkSuQmCC\" width=\"600\" height=\"400\"><\/div><br \/>\n<\/center><\/p>\n<p>&emsp;&emsp;\u5982\u4e0b\u662f\u8282\u9009\u7684BL\u5f15\u811a\u7684sysfs\u7684\u521d\u59cb\u5316\u8bbe\u5907\u6811\u5199\u6cd5\u3002<\/p>\n<pre><code class=\"language-c\">\/ {\n    model = \"Luckfox Pico Plus\";\n    compatible = \"rockchip,rv1103g-38x38-ipc-v10\", \"rockchip,rv1103\";\n    \/*LCD_BL*\/\n    gpio0pa4:gpio0pa4 {\n        compatible = \"regulator-fixed\";\n        pinctrl-names = \"default\";\n        pinctrl-0 = <&#038;gpio0_pa4>;\n        regulator-name = \"gpio0_pa4\";\n        regulator-always-on;\n    };\n}\n&pinctrl {\n    \/*LCD_BL*\/\n    gpio0-pa4 {\n        gpio0_pa4:gpio0-pa4 {\n            rockchip,pins = <0 RK_PA4 RK_FUNC_GPIO &#038;pcfg_pull_none>;\n        };\n    };\n}<\/code><\/pre>\n<p>&emsp;&emsp;\u7efc\u4e0a\u6240\u8ff0\u53ef\u4ee5\u770b\u51fa\u5b98\u65b9demo\u5176\u5b9e\u6838\u5fc3\u601d\u8def\u5c31\u662f\u8c03\u7528spi\u5b50\u7cfb\u7edf\u4f20\u8f93\u6570\u636e\uff0c\u5176\u4ed6\u4ee3\u7801\u90fd\u662f\u56f4\u7ed5\u8fd9\u4e2a\u6838\u5fc3\u7f16\u5199\u7684\uff0c\u628a\u63e1\u8fd9\u4e2a\u601d\u8def\uff0c\u6574\u4f53\u8bfb\u4e0b\u6765\u5c31\u5f88\u6e05\u6670\u660e\u4e86\u4e86\u3002<\/p>\n<h1>3 framebuffer\u9a71\u52a8<\/h1>\n<p>&emsp;&emsp;\u60f3\u8981\u8bfb\u61c2framebuffer\u9a71\u52a8\u7684\u4ee3\u7801\u4e3b\u8981\u770b\u4e24\u4e2a\u6a21\u5757\uff0c\u4e00\u4e2a\u662f\u521d\u59cb\u5316\u51fd\u6570\uff0c\u4e00\u4e2a\u662f\u64cd\u4f5c\u65b9\u6cd5\u7ed3\u6784\u4f53\u3002\u901a\u8fc7prob\u5165\u53e3\u51fd\u6570\u53ef\u4ee5\u4e86\u89e3\u6240\u6709\u53d8\u91cf\u53ca\u51fd\u6570\u7684\u6ce8\u518c\u53ca\u521d\u59cb\u5316\u6d41\u7a0b\uff1b\u901a\u8fc7ops\u64cd\u4f5c\u65b9\u6cd5\u7ed3\u6784\u4f53\u53ef\u4ee5\u627e\u5230\u6240\u6709\u8c03\u7528read\u3001write\u7b49\u63a5\u53e3\u51fd\u6570\u4e0e\u5e94\u7528\u5c42\u4ea4\u4e92\u7684\u903b\u8f91\uff1b\u4e8c\u8005\u7ed3\u5408\u5f88\u5bb9\u6613\u5c31\u80fd\u627e\u5230\u601d\u8def\u8bfb\u61c2\u6574\u4e2a\u9a71\u52a8\u3002<br \/>\n&emsp;&emsp;\u6574\u4f53\u8bfb\u4e0b\u6765\u5b98\u65b9\u7684framebuffer\u9a71\u52a8\uff0c\u603b\u7ed3\u5176\u6838\u5fc3\u51fd\u6570\u5c31\u662ffb_mmap\u51fd\u6570\uff1b\u4e0e\u786c\u4ef6\u4ea4\u4e92\u7684\u6838\u5fc3\u8fd8\u662fspi\u5b50\u7cfb\u7edf\u90a3\u5957\u51fd\u6570\uff1b\u5269\u4f59\u7684\u5173\u952e\u70b9\u5c31\u662f\u521d\u59cb\u5316lcd\u5c4f\u5e55\u5f97\u90a3\u5957\u903b\u8f91\u4e86\u3002\u603b\u5f97\u770b\u5199\u6cd5\u5f88\u590d\u6742\uff0c\u4f46\u4e1c\u897f\u8fd8\u662f\u90a3\u4e9b\u4e1c\u897f\u3002<\/p>\n<h2>3.1 framebuffer\u5b50\u7cfb\u7edf<\/h2>\n<h3>3.1.1 fb_mmap\u51fd\u6570<\/h3>\n<p>&emsp;&emsp;\u901a\u8fc7\u6211\u4e0a\u9762\u7684\u5410\u69fd\u4e5f\u80fd\u770b\u51fa\uff0c\u5173\u4e8eframebuffer\u7684\u95ee\u9898\u6700\u7ec8\u90fd\u6307\u5411\u4e86fb_mmap\u8fd9\u4e2a\u51fd\u6570\u3002<br \/>\n&emsp;&emsp;\u5982\u4e0b\u662fframmebuffer\u5b50\u7cfb\u7edf\u5728\/video\/fbdev\/core\/fbmem.c\u6587\u4ef6\u4e2d\u5bf9fb_mmap\u7684\u51fd\u6570\u5b9e\u73b0\u3002\u901a\u8fc7\u8fd9\u4e2a\u51fd\u6570\uff0c\u5185\u6838\u5b9e\u73b0\u4e86\u5bf9\u7a7a\u95f4\u7684\u5206\u914d\uff0c\u4ee5\u53ca\u5c06\u5730\u5740\u4f20\u9012\u7ed9\u5e94\u7528\u5c42\u63a5\u53e3\u7684\u529f\u80fd\u3002<\/p>\n<h4>3.1.1.1 \u51fd\u6570\u903b\u8f91\u5206\u6790<\/h4>\n<p>&emsp;&emsp;\u8fd9\u4e2a\u51fd\u6570\u5728\u4ee3\u7801\u521d\u59cb\u9636\u6bb5\u5c31\u4f7f\u7528file_fb_info(file)\u51fd\u6570\u83b7\u53d6fb_info\u7684\u7ed3\u6784\u4f53\u3002\u82e5\u5728\u9a71\u52a8\u4e2d\u5df2\u7ecf\u5b9e\u73b0\u4e86\u81ea\u5df1\u7684\u64cd\u4f5c\u65b9\u6cd5\u7ed3\u6784\u4f53\uff0c\u5219\u4f1a\u8fd4\u56de\u6307\u9488\uff0c\u5728\u4e0b\u9762\u505a\u8fdb\u4e00\u6b65\u5904\u7406\u3002\u6ca1\u6709\u5219\u6267\u884c\u5b98\u65b9\u81ea\u5df1\u7684\u903b\u8f91\u3002<br \/>\n&emsp;&emsp;\u6309\u9053\u7406\u6211\u81ea\u5df1\u5728fb_ops\u7684\u64cd\u4f5c\u65b9\u6cd5\u7ed3\u6784\u4f53\u4e2d\u4e5f\u5b9e\u73b0\u4e86\u81ea\u5df1\u7684fb_mmap\u51fd\u6570\uff0c\u5e94\u8be5\u4f1a\u5728\u6267\u884c\u9636\u6bb5\u8c03\u7528\u6211\u7684\u51fd\u6570\uff0c\u5374\u6ca1\u8c03\u7528\u6210\u529f\uff1b\u6700\u540e\u6000\u7591\u662f\u81ea\u5df1\u5b9e\u73b0\u51fd\u6570\u7684\u53c2\u6570\u548c\u7c7b\u578b\u4e0d\u5bf9\uff0c\u5bfc\u81f4\u5728\u540e\u7eed\u6267\u884c\u65f6\u53d1\u751f\u4e86\u5206\u914d\u7a7a\u95f4\u7684\u9519\u8bef\u3002<\/p>\n<pre><code class=\"language-c\">static int\nfb_mmap(struct file *file, struct vm_area_struct * vma)\n{\n        \/\/\u83b7\u53d6\u9a71\u52a8\u7684\u64cd\u4f5c\u65b9\u6cd5\u7ed3\u6784\u4f53\uff0c\u5e76\u8d4b\u7ed9info\n        struct fb_info *info = file_fb_info(file);\n        int (*fb_mmap_fn)(struct fb_info *info, struct vm_area_struct *vma);\n        unsigned long mmio_pgoff;\n        unsigned long start;\n        u32 len;\n        \/\/\u5224\u65ad\uff0cinfo\u4e3a\u7a7a\uff0c\u8fd4\u56de\u9519\u8bef\n        if (!info)\n                return -ENODEV;\n        mutex_lock(&info->mm_lock);\n        \/\/\u65e0\u8bba\u9a71\u52a8\u4e2d\u662f\u5426\u5b9e\u73b0\u4e86fb_mmap\uff0c\u76f4\u63a5\u8d4b\u503c\uff0c\u4e3a\u7a7a\u4e5f\u6ca1\u95ee\u9898\u3002\n        fb_mmap_fn = info->fbops->fb_mmap;\n\/\/\u4f18\u5316\u9009\u9879\uff0c\u82e5\u5728config\u6587\u4ef6\u4e2d\u914d\u7f6e\uff0c\u5219\u4f1a\u4f7f\u7528\u5185\u6838\u7684mmap\u51fd\u6570\u3002\n#if IS_ENABLED(CONFIG_FB_DEFERRED_IO)\n        if (info->fbdefio)\n                fb_mmap_fn = fb_deferred_io_mmap;\n#endif\n        \/\/\u8fd9\u91cc\u5bf9\u8d4b\u503c\u7684fb_mmap_fn\u51fd\u6570\u8fdb\u884c\u5224\u65ad\uff0c\u82e5\u5199\u4e86\u81ea\u5df1\u7684mmap\u51fd\u6570\uff0c\u5219\u76f4\u63a5\u8fdb\u5165\uff0c\u5426\u5219\u6267\u884c\u4e0b\u4e00\u6b65   \n        if (fb_mmap_fn) {\n                int res;\n\n                \/*\n                 * The framebuffer needs to be accessed decrypted, be sure\n                 * SME protection is removed ahead of the call\n                 *\/\n                vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot);\n                res = fb_mmap_fn(info, vma);\n                mutex_unlock(&info->mm_lock);\n                return res;\n        }\n\n        \/*\n         * Ugh. This can be either the frame buffer mapping, or\n         * if pgoff points past it, the mmio mapping.\n         *\/\n         \/\/\u5bf9\u4e8e\u6ca1\u5b9e\u73b0\u81ea\u5df1mmap\u7684\u51fd\u6570\uff0c\u5219\u6b63\u5e38\u6267\u884c\u7cfb\u7edf\u81ea\u5e26\u7684mmap\u7684\u903b\u8f91\n        start = info->fix.smem_start;\n        len = info->fix.smem_len;\n        mmio_pgoff = PAGE_ALIGN((start & ~PAGE_MASK) + len) >> PAGE_SHIFT;\n        if (vma->vm_pgoff >= mmio_pgoff) {\n                if (info->var.accel_flags) {\n                        mutex_unlock(&info->mm_lock);\n                        return -EINVAL;\n                }\n\n                vma->vm_pgoff -= mmio_pgoff;\n                start = info->fix.mmio_start;\n                len = info->fix.mmio_len;\n        }\n        mutex_unlock(&info->mm_lock);\n\n        vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);\n        \/*\n         * The framebuffer needs to be accessed decrypted, be sure\n         * SME protection is removed\n         *\/\n        vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot);\n        fb_pgprotect(file, vma, start);\n\n        return vm_iomap_memory(vma, start, len);\n}<\/code><\/pre>\n<h4>3.1.1.2 \u51fd\u6570\u529f\u80fd\u5b9e\u73b0<\/h4>\n<p>&emsp;&emsp;\u641e\u61c2\u5185\u6838\u5bf9mmap\u51fd\u6570\u7684\u5904\u7406\u903b\u8f91\u540e\uff0c\u5bf9\u51fd\u6570\u505a\u8fdb\u4e00\u6b65\u5206\u6790\uff0c\u4e86\u89e3\u5173\u952e\u5206\u914d\u5185\u5b58\u7a7a\u95f4\u7684\u6d41\u7a0b\u3002<\/p>\n<h5>3.1.1.2.1 \u5185\u5b58\u6743\u9650\u5206\u914d<\/h5>\n<p>&emsp;&emsp;vma-&gt;vm_page_prot\u662f\u7528\u6765\u63cf\u8ff0\u6620\u5c04\u533a\u57df\u7684\u5185\u5b58\u9875\u6743\u9650\u3002\u8fd9\u91cc\u901a\u8fc7\u8c03\u7528 pgprot_decrypted()\u51fd\u6570\uff0c\u786e\u4fdd\u6620\u5c04\u533a\u57df\u4e0d\u5305\u542bSME\uff08Secure Memory Encryption\uff09\u4fdd\u62a4\u3002<br \/>\n&emsp;&emsp;SME\u662f\u4e00\u79cd\u5185\u5b58\u52a0\u5bc6\u6280\u672f\uff0c\u65e8\u5728\u901a\u8fc7\u786c\u4ef6\u7ea7\u7684\u5185\u5b58\u52a0\u5bc6\u6765\u4fdd\u62a4\u7cfb\u7edf\u4e2d\u7684\u654f\u611f\u6570\u636e\u3002\u67d0\u4e9b\u5e73\u53f0\uff08\u4f8b\u5982 AMD EPYC \u5904\u7406\u5668\uff09\u652f\u6301SME\u3002<br \/>\n&emsp;&emsp;pgprot_decrypted()\u51fd\u6570\u4f1a\u79fb\u9664SME\u4fdd\u62a4\uff0c\u4f7f\u5f97\u5185\u5b58\u53ef\u4ee5\u5728\u672a\u52a0\u5bc6\u7684\u60c5\u51b5\u4e0b\u8bbf\u95ee\u3002\u8fd9\u4e00\u6b65\u786e\u4fdd\u6620\u5c04\u7684 Framebuffer\u5185\u5b58\u662f\u53ef\u89e3\u5bc6\u8bbf\u95ee\u7684\u3002<\/p>\n<pre><code class=\"language-c\">vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot);<\/code><\/pre>\n<h5>3.1.1.2.2 \u6620\u5c04\u5730\u5740\u7a7a\u95f4<\/h5>\n<p>&emsp;&emsp;\u8fd9\u6bb5\u4ee3\u7801\u662f Linux \u5185\u6838\u4e2d fb_mmap \u51fd\u6570\u7684\u4e00\u90e8\u5206\uff0c\u7528\u4e8e\u5904\u7406 framebuffer \u663e\u5b58\uff08framebuffer memory\uff09\u548c MMIO\uff08\u5185\u5b58\u6620\u5c04 I\/O\uff09\u533a\u57df\u7684\u5185\u5b58\u6620\u5c04\u3002\u5b83\u51b3\u5b9a\u4e86\u7528\u6237\u7a7a\u95f4\u8bf7\u6c42 mmap \u65f6\uff0c\u5982\u4f55\u5c06 framebuffer \u6216\u8bbe\u5907\u7684 MMIO \u6620\u5c04\u5230\u7528\u6237\u8fdb\u7a0b\u7684\u5730\u5740\u7a7a\u95f4\u3002<\/p>\n<pre><code class=\"language-c\">\/\/1.\u63d0\u53d6smem_start\u548csmem_len\nstart = info->fix.smem_start;\nlen = info->fix.smem_len;\n\/\/info->fix.smem_start\uff1a\u8fd9\u662f framebuffer\u7684\u663e\u5b58\u8d77\u59cb\u5730\u5740\uff0c\u901a\u5e38\u8868\u793a\u8bbe\u5907\u4e0a\u663e\u5b58\u7684\u7269\u7406\u5730\u5740\u3002\n\/\/info->fix.smem_len\uff1a\u663e\u5b58\u7684\u957f\u5ea6\uff08\u5373\u663e\u5b58\u7684\u5927\u5c0f\uff09\u3002\n\/\/\u8fd9\u4e9b\u4fe1\u606f\u6765\u81ea\u4e8efb_info->fix\u7ed3\u6784\u4f53\uff0c\u901a\u5e38\u662f\u5728\u521d\u59cb\u5316framebuffer\u65f6\u7531\u9a71\u52a8\u8bbe\u7f6e\u7684\u3002\n\n\/\/2.\u8ba1\u7b97mmio_pgoff\uff08MMIO \u504f\u79fb\u91cf\uff09\nmmio_pgoff = PAGE_ALIGN((start & ~PAGE_MASK) + len) >> PAGE_SHIFT;\n\/\/\u8fd9\u91cc\u7684PAGE_ALIGN\u662f\u7528\u6765\u5c06\u8d77\u59cb\u5730\u5740\u5bf9\u9f50\u5230\u9875\u9762\u5927\u5c0f\uff08\u901a\u5e38\u662f 4KB\uff09\u3002\n\/\/start&~PAGE_MASK\u4f1a\u79fb\u9664\u4f4e\u4f4d\u5730\u5740\u4e2d\u7684\u9875\u9762\u504f\u79fb\u91cf\uff08\u5373\u53ea\u4fdd\u7559\u9875\u9762\u5bf9\u9f50\u7684\u90e8\u5206\uff09\u3002\n\/\/\u5c06\u663e\u5b58\u5730\u5740start\u548c\u957f\u5ea6len\u5bf9\u9f50\u540e\uff0c\u8ba1\u7b97\u5bf9\u5e94\u7684\u9875\u9762\u504f\u79fb\u91cf\uff0c\u5e76\u901a\u8fc7>>PAGE_SHIFT\u5c06\u5b57\u8282\u504f\u79fb\u91cf\u8f6c\u5316\u4e3a\u9875\u504f\u79fb\u91cf\u3002\n\/\/mmio_pgoff\u8868\u793aMMIO\u533a\u57df\u7684\u9875\u504f\u79fb\u91cf\u3002MMIO \u662f\u5185\u5b58\u6620\u5c04I\/O\uff0c\u7528\u4e8e\u8bbf\u95ee\u786c\u4ef6\u5bc4\u5b58\u5668\u6216\u5176\u4ed6\u975e\u6807\u51c6\u5185\u5b58\u533a\u57df\u3002\n\n\/\/3.\u5224\u65ad\u662f\u5426\u4e3a MMIO \u533a\u57df\nif (vma->vm_pgoff >= mmio_pgoff) {\n\/\/vma->vm_pgoff\uff1a\u8fd9\u4e2a\u662f\u7528\u6237\u8bf7\u6c42 mmap \u65f6\u4f20\u9012\u7684\u9875\u9762\u504f\u79fb\u91cf\uff0c\u8868\u793a\u7528\u6237\u60f3\u6620\u5c04\u7684\u5185\u5b58\u533a\u57df\u7684\u8d77\u59cb\u5730\u5740\u3002\n\/\/\u5982\u679c\u7528\u6237\u8bf7\u6c42\u7684\u9875\u9762\u504f\u79fb\u91cf\u5927\u4e8e\u7b49\u4e8e mmio_pgoff\uff0c\u610f\u5473\u7740\u7528\u6237\u60f3\u8981\u8bbf\u95ee\u7684\u5185\u5b58\u8d85\u51fa\u4e86 framebuffer \u663e\u5b58\u533a\u57df\uff0c\u53ef\u80fd\u5728\u8bf7\u6c42 MMIO \u533a\u57df\u3002\n\n\/\/4.\u5904\u7406 MMIO \u533a\u57df\u7684\u6620\u5c04\nif (info->var.accel_flags) {\n    mutex_unlock(&info->mm_lock);\n    return -EINVAL;\n}\n\/\/\u5982\u679c\u8bbe\u5907\u6709\u786c\u4ef6\u52a0\u901f\u529f\u80fd\uff08info->var.accel_flags \u8868\u793a\u52a0\u901f\u6807\u5fd7\u4f4d\uff09\uff0c\u5219\u4e0d\u5141\u8bb8\u6620\u5c04 MMIO \u533a\u57df\uff0c\u8fd4\u56de\u9519\u8bef -EINVAL\u3002\nvma->vm_pgoff -= mmio_pgoff;\nstart = info->fix.mmio_start;\nlen = info->fix.mmio_len;\n\n\/\/\u5982\u679c\u6ca1\u6709\u786c\u4ef6\u52a0\u901f\uff0c\u5219\u5904\u7406 MMIO \u533a\u57df\u7684\u6620\u5c04\u3002\n\/\/vma->vm_pgoff -= mmio_pgoff\uff1a\u8c03\u6574\u504f\u79fb\u91cf\uff0c\u51cf\u53bb\u663e\u5b58\u533a\u57df\u7684\u9875\u504f\u79fb\u91cf\uff0c\u4ee5\u4fbf\u63a5\u4e0b\u6765\u6620\u5c04 MMIO \u533a\u57df\u3002\n\/\/\u5c06 start \u8bbe\u7f6e\u4e3a info->fix.mmio_start\uff08MMIO \u7684\u8d77\u59cb\u5730\u5740\uff09\uff0c\u5c06 len \u8bbe\u7f6e\u4e3a info->fix.mmio_len\uff08MMIO \u7684\u957f\u5ea6\uff09\uff0c\u4ece\u800c\u51c6\u5907\u6620\u5c04 MMIO \u5185\u5b58\u3002\n\n\/\/5.\u89e3\u9501\u4e92\u65a5\u9501\nmutex_unlock(&info->mm_lock);\n\/\/\u4e4b\u524d\u5728\u51fd\u6570\u5f00\u59cb\u65f6\u9501\u5b9a\u4e86 info->mm_lock\uff0c\u73b0\u5728\u9700\u8981\u89e3\u9501\u3002\u8be5\u9501\u786e\u4fdd\u5bf9 info \u7ed3\u6784\u7684\u8bbf\u95ee\u662f\u7ebf\u7a0b\u5b89\u5168\u7684\u3002\n\n\/\/6.\u8bbe\u7f6e\u5185\u5b58\u9875\u6743\u9650\nvma->vm_page_prot = vm_get_page_prot(vma->vm_flags);\nvma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot);\n\/\/vm_get_page_prot(vma->vm_flags)\uff1a\u6839\u636e vma \u7684\u6807\u5fd7\u4f4d\uff0c\u83b7\u53d6\u5408\u9002\u7684\u5185\u5b58\u9875\u9762\u6743\u9650\u3002\n\/\/pgprot_decrypted(vma->vm_page_prot)\uff1a\u786e\u4fdd SME\uff08Secure Memory Encryption\uff09\u4fdd\u62a4\u88ab\u79fb\u9664\uff0c\u4ee5\u5141\u8bb8\u672a\u52a0\u5bc6\u8bbf\u95ee\u3002\u8fd9\u662f\u4e3a\u4e86\u9002\u914d\u67d0\u4e9b\u786c\u4ef6\u7684\u52a0\u5bc6\u673a\u5236\uff0c\u786e\u4fdd\u5185\u5b58\u53ef\u4ee5\u76f4\u63a5\u8bbf\u95ee\u3002\n\n\/\/7.\u4fdd\u62a4 framebuffer \u6620\u5c04\nfb_pgprotect(file, vma, start);\n\/\/\u8be5\u51fd\u6570\u786e\u4fdd\u6620\u5c04\u7684 framebuffer \u6216 MMIO \u533a\u57df\u5177\u5907\u9002\u5f53\u7684\u4fdd\u62a4\u63aa\u65bd\uff08\u5982\u53ea\u8bfb\u6216\u53ef\u8bfb\u5199\u6743\u9650\uff09\u3002fb_pgprotect \u662f\u4e00\u4e2a\u8f85\u52a9\u51fd\u6570\uff0c\u7528\u4e8e\u8c03\u6574\u9875\u8868\u7684\u6743\u9650\u3002\n\n\/\/8.\u5c06\u5185\u5b58\u6620\u5c04\u5230\u7528\u6237\u7a7a\u95f4\nreturn vm_iomap_memory(vma, start, len);\n\/\/vm_iomap_memory\uff1a\u5c06\u7269\u7406\u5185\u5b58\uff08start \u5230 start+len\uff09\u6620\u5c04\u5230\u7528\u6237\u8fdb\u7a0b\u7684\u5730\u5740\u7a7a\u95f4\u3002\u8fd9\u662f\u4e00\u4e2a\u901a\u7528\u7684\u5185\u6838\u51fd\u6570\uff0c\u5904\u7406\u5c06\u7269\u7406\u5185\u5b58\u9875\u6620\u5c04\u5230\u865a\u62df\u5185\u5b58\u3002<\/code><\/pre>\n<h3>3.1.2 \u9a71\u52a8\u63a5\u53e3\u6ce8\u518c<\/h3>\n<p>&emsp;&emsp;\u5f53framebuffer\u9a71\u52a8\u88ab\u52a0\u8f7d\u65f6\uff0c\u9a71\u52a8\u4f1a\u8c03\u7528 register_framebuffer()\u51fd\u6570\u6765\u6ce8\u518cframebuffer\u8bbe\u5907\u3002\u5728\u8fd9\u4e2a\u8fc7\u7a0b\u4e2d\uff0c\/dev\/fb0 \u8bbe\u5907\u8282\u70b9\u4e5f\u4f1a\u81ea\u52a8\u521b\u5efa\u3002<\/p>\n<pre><code class=\"language-c\">int register_framebuffer(struct fb_info *fb_info)<\/code><\/pre>\n<h3>3.1.3 \u5e94\u7528\u5c42\u63a5\u53e3\u793a\u4f8b<\/h3>\n<p>&emsp;&emsp;\u5728\u9a71\u52a8\u6210\u529f\u6ce8\u518cfb0\u63a5\u53e3\u540e\uff0c\u5e94\u7528\u5c42\u5373\u53ef\u5229\u7528\u6b64\u63a5\u53e3\u83b7\u53d6\u6587\u4ef6\u63cf\u8ff0\u7b26\uff0c\u8fdb\u4e00\u6b65\u5229\u7528mmap\u5728\u5e94\u7528\u5c42\u7684\u63a5\u53e3\u83b7\u53d6\u5206\u914d\u7684\u5185\u5b58\u7a7a\u95f4\u7684\u5730\u5740\uff0c\u4f20\u8f93\u56fe\u50cf\u6570\u636e\u7ed9\u5185\u6838\u3002\u5982\u4e0b\u662f\u5e94\u7528\u5c42\u6d4b\u8bd5\u7528\u4f8b\uff1a<\/p>\n<pre><code class=\"language-c\">#include &ltstdio.h&gt\n#include &ltstdlib.h&gt\n#include &ltfcntl.h&gt\n#include &ltunistd.h&gt\n#include &ltsys\/mman.h&gt\n#include &ltlinux\/fb.h&gt\n#include &ltsys\/ioctl.h&gt\n#include &ltstdint.h&gt\n#include &ltstring.h&gt\n\n\/\/ \u5b9a\u4e49\u989c\u8272\n#define RED     0x00FF0000\n#define GREEN   0x0000FF00\n#define BLUE    0x000000FF\n#define WHITE   0x00FFFFFF\n#define BLACK   0x00000000\n\n\/\/ framebuffer \u7ed3\u6784\nstruct framebuffer_info {\n    uint32_t width;   \/\/ \u5c4f\u5e55\u5bbd\u5ea6\n    uint32_t height;  \/\/ \u5c4f\u5e55\u9ad8\u5ea6\n    uint32_t bpp;     \/\/ \u6bcf\u50cf\u7d20\u4f4d\u6570\n    size_t screensize; \/\/ \u6620\u5c04\u5185\u5b58\u7684\u5927\u5c0f\n    uint32_t *fbp;    \/\/ framebuffer\u6307\u9488\n};\n\nstruct framebuffer_info get_framebuffer_info(int fb_fd) {\n    struct framebuffer_info fb_info;\n    struct fb_var_screeninfo vinfo;\n    struct fb_fix_screeninfo finfo;\n\n    \/\/ \u83b7\u53d6\u56fa\u5b9a\u7684\u5c4f\u5e55\u4fe1\u606f\n    ioctl(fb_fd, FBIOGET_FSCREENINFO, &finfo);\n    \/\/ \u83b7\u53d6\u53ef\u53d8\u7684\u5c4f\u5e55\u4fe1\u606f\n    ioctl(fb_fd, FBIOGET_VSCREENINFO, &vinfo);\n\n    fb_info.width = vinfo.xres;\n    fb_info.height = vinfo.yres;\n    fb_info.bpp = vinfo.bits_per_pixel;\n    fb_info.screensize = finfo.smem_len;\n\n    return fb_info;\n}\n\nvoid draw_color(struct framebuffer_info *fb_info, uint32_t color) {\n    size_t pixels = fb_info->width * fb_info->height;\n    for (size_t i = 0; i < pixels; ++i) {\n        fb_info->fbp[i] = color;\n    }\n}\n\nint main() {\n    int fb_fd = open(\"\/dev\/fb0\", O_RDWR);\n    if (fb_fd == -1) {\n        perror(\"Error: cannot open framebuffer device\");\n        exit(1);\n    }\n\n    struct framebuffer_info fb_info = get_framebuffer_info(fb_fd);\n\n    \/\/ \u4f7f\u7528 mmap \u6620\u5c04 framebuffer \u5230\u5185\u5b58\n    fb_info.fbp = (uint32_t*)mmap(0, fb_info.screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, 0);\n    if ((intptr_t)fb_info.fbp == -1) {\n        perror(\"Error: failed to mmap\");\n        close(fb_fd);\n        exit(1);\n    }\n\n    \/\/ \u663e\u793a\u7ea2\u8272\n    draw_color(&fb_info, RED);\n    sleep(2);  \/\/ \u663e\u793a 2 \u79d2\n\n    \/\/ \u663e\u793a\u7eff\u8272\n    draw_color(&fb_info, GREEN);\n    sleep(2);  \/\/ \u663e\u793a 2 \u79d2\n\n    \/\/ \u663e\u793a\u84dd\u8272\n    draw_color(&fb_info, BLUE);\n    sleep(2);  \/\/ \u663e\u793a 2 \u79d2\n\n    \/\/ \u6e05\u9664\u5c4f\u5e55\uff0c\u663e\u793a\u9ed1\u8272\n    draw_color(&fb_info, BLACK);\n    sleep(2);  \/\/ \u663e\u793a 2 \u79d2\n\n    \/\/ \u53d6\u6d88\u6620\u5c04\u5e76\u5173\u95ed framebuffer \u8bbe\u5907\n    munmap(fb_info.fbp, fb_info.screensize);\n    close(fb_fd);\n\n    return 0;\n}<\/code><\/pre>\n<h2>3.2 framebuffer\u9a71\u52a8\u7684\u989c\u8272\u663e\u793a\u6a21\u5757<\/h2>\n<p>&emsp;&emsp;\u5e94\u7528\u5c42\u5230\u9a71\u52a8\u7684\u8def\u901a\u4e86\u4ee5\u540e\uff0c\u63a5\u7740\u5206\u6790\u9a71\u52a8\u5230\u786c\u4ef6\u7684\u8def\u3002<br \/>\n&emsp;&emsp;\u4f20\u8f93\u6570\u636e\u524d\u9996\u8981\u7684\u5de5\u4f5c\u5c31\u662f\u5bf9\u5e94\u7528\u5c42\u7684\u6570\u636e\u8fdb\u884c\u5904\u7406\uff0c\u800c\u8fd9\u6bb5\u5904\u7406\u7a0b\u5e8f\u6070\u6070\u4e5f\u662f\u6574\u4e2a\u9a71\u52a8\u4e2d\u6700\u7410\u788e\u7684\u4ee3\u7801\u3002\u4e0d\u96be\u4f46\u6df7\u4e71\uff0c\u8bfb\u4ee3\u7801\u65f6\u8981\u9891\u7e41\u7684\u8df3\u8f6c\u5bfb\u627e\u5176\u8c03\u7528\u903b\u8f91\uff0c\u6765\u7406\u89e3\u9a71\u52a8\u4f5c\u8005\u7f16\u5199\u7684\u601d\u8def\u3002<\/p>\n<h3>3.2.1 \u9a71\u52a8\u4e2d\u7684\u7ed3\u6784\u4f53<\/h3>\n<p>&emsp;&emsp;\u7406\u89e3\u4ee3\u7801\u7684\u7b2c\u4e00\u6b65\uff0c\u5206\u6790\u9a71\u52a8\u7684\u6570\u636e\u7ed3\u6784\u4f53\u3002\u5982\u4e0b\u662f\u9a71\u52a8\u81ea\u8eab\u7f16\u5199\u7684\u7ed3\u6784\u4f53\u3002<\/p>\n<pre><code class=\"language-c\">    struct fbtft_par {\n    struct spi_device *spi;\/\/spi\u6307\u9488\n    struct platform_device *pdev;\n    struct fb_info *info;\/\/\u5185\u6838\u7684fb\u7684\u4fe1\u606f\u7ed3\u6784\u4f53\n    struct fbtft_platform_data *pdata;\n    u32 pseudo_palette[16];\n    struct {\n        void *buf;\n        size_t len;\n    } txbuf;\n    u8 *buf;\n    struct fbtft_ops fbtftops;\/\/\u64cd\u4f5c\u65b9\u6cd5\u7ed3\u6784\u4f53\u6307\u9488\n    struct {\n        struct mutex lock;\n        u32 *curves;\n        int num_values;\n        int num_curves;\n    } gamma;\n    bool bgr;\/\/\u7ea2\u84dd\u53cd\u7f6e\u6807\u5fd7\u4f4d\n    void *extra;\n    bool polarity;\n};<\/code><\/pre>\n<p>&emsp;&emsp;\u5982\u4e0b\u662f\u5185\u6838frammebuffer\u5b50\u7cfb\u7edf\u7684\u7ed3\u6784\u4f53\uff0c\u5b58\u50a8\u7740\u5b50\u7cfb\u7edf\u7684\u5404\u7c7b\u4fe1\u606f\u3002<\/p>\n<pre><code class=\"language-c\">struct fb_info {\n    int node;                           \/\/ framebuffer \u7684\u7f16\u53f7\uff0c\u4f8b\u5982 \/dev\/fb0 \u5219 node \u4e3a 0\n    int flags;                          \/\/ \u6807\u5fd7\u4f4d\uff0c\u63a7\u5236\u8bbe\u5907\u884c\u4e3a\n    struct mutex lock;                  \/\/ \u4fdd\u62a4 fb_info \u7684\u9501\n    struct mutex mm_lock;               \/\/ \u6620\u5c04\u64cd\u4f5c (mmap) \u7684\u9501\n    struct fb_var_screeninfo var;       \/\/ \u53ef\u53d8\u5c4f\u5e55\u4fe1\u606f\uff0c\u5982\u5206\u8fa8\u7387\u3001\u8272\u6df1\u7b49\n    struct fb_fix_screeninfo fix;       \/\/ \u56fa\u5b9a\u5c4f\u5e55\u4fe1\u606f\uff0c\u5982\u663e\u5b58\u5730\u5740\u7b49\n    struct fb_monspecs monspecs;        \/\/ \u663e\u793a\u5668\u7684\u89c4\u683c\u4fe1\u606f\n    struct fb_pixmap pixmap;            \/\/ \u4e00\u4e9b\u56fe\u7247\u7f13\u51b2\u76f8\u5173\u4fe1\u606f\n    struct fb_ops *fbops;               \/\/ \u6307\u5411 framebuffer \u64cd\u4f5c\u51fd\u6570\u7684\u6307\u9488\uff08\u5305\u62ec mmap\u3001read\u3001write \u7b49\uff09\n    struct device *device;              \/\/ \u6307\u5411\u8be5 framebuffer \u8bbe\u5907\u7684\u6307\u9488\n    struct fb_deferred_io *fbdefio;     \/\/ \u5ef6\u8fdf IO \u76f8\u5173\u7ed3\u6784\u4f53\uff08\u5982\u679c\u652f\u6301\u7684\u8bdd\uff09\n    void *pseudo_palette;               \/\/ \u7528\u4e8e\u4f2a\u8c03\u8272\u677f\u7684\u5b58\u50a8\n    void *par;                          \/\/ \u9a71\u52a8\u7a0b\u5e8f\u7684\u79c1\u6709\u6570\u636e\n    unsigned char *screen_base;         \/\/ \u663e\u5b58\u7684\u8d77\u59cb\u5730\u5740\n    unsigned long screen_size;          \/\/ \u663e\u5b58\u7684\u5927\u5c0f\n    \/\/ ... \u5176\u4ed6\u6210\u5458\n};<\/code><\/pre>\n<h3>3.2.2 \u9a71\u52a8\u7684\u521d\u59cb\u5316<\/h3>\n<p>&emsp;&emsp;\u9a71\u52a8\u7684prob\u5165\u53e3\u662f\u5339\u914d\u6210\u529f\u540e\u8fdb\u5165\u7684\u4e3b\u51fd\u6570\uff0c\u56e0\u6b64\u4e00\u7cfb\u5217\u521d\u59cb\u5316\u64cd\u4f5c\u5747\u5728\u8fd9\u4e2a\u51fd\u6570\u4e2d\u8fdb\u884c\u3002\u5982\u4e0b\u662f\u622a\u53d6\u7684\u5173\u952e\u51fd\u6570\u90e8\u5206\uff1a<\/p>\n<pre><code class=\"language-c\">int fbtft_probe_common(struct fbtft_display *display,\n               struct spi_device *sdev,\n               struct platform_device *pdev)\n{\n    struct device *dev;\n    struct fb_info *info;\n    struct fbtft_par *par;\n    struct fbtft_platform_data *pdata;\n    info = fbtft_framebuffer_alloc(display, dev, pdata); \/\/\u521b\u5efa\u4e00\u4e2a\u65b0\u7684\u5e27\u7f13\u51b2\u533a\u4fe1\u606f\u7ed3\u6784\uff0c\u5206\u914d\u7a7a\u95f4\u7b49\u64cd\u4f5c<\/code><\/pre>\n<p>&emsp;&emsp;\u7531\u6b64\u8fdb\u5165\u521d\u59cb\u5316info\u7684\u7ed3\u6784\u4f53\uff0c\u521d\u59cb\u5316\u5404\u9879\u53c2\u6570<\/p>\n<pre><code class=\"language-c\">struct fb_info *fbtft_framebuffer_alloc(struct fbtft_display *display,\n                    struct device *dev,\n                    struct fbtft_platform_data *pdata)\n{\n    struct fb_info *info;\n    struct fbtft_par *par;\n    struct fb_ops *fbops = NULL;\/\/\u5206\u914d\u5e27\u7f13\u51b2\u8bbe\u5907\n    info = framebuffer_alloc(sizeof(struct fbtft_par), dev);\n\n    fbops->owner        =      dev->driver->owner;\n    fbops->fb_read      =      fb_sys_read;\n    fbops->fb_write     =      fbtft_fb_write;\n    fbops->fb_fillrect  =      fbtft_fb_fillrect;\n    fbops->fb_copyarea  =      fbtft_fb_copyarea;\n    fbops->fb_imageblit =      fbtft_fb_imageblit;\n    fbops->fb_setcolreg =      fbtft_fb_setcolreg;\n    fbops->fb_blank     =      fbtft_fb_blank;\n\n    fbdefio->delay =           HZ \/ fps;\n    fbdefio->deferred_io =     fbtft_deferred_io;\n    fb_deferred_io_init(info);\n\/\/\u521d\u59cb\u5316\u7ed3\u6784\u4f53\u7684\u56fa\u5b9a\u53c2\u6570\n    snprintf(info->fix.id, sizeof(info->fix.id), \"%s\", dev->driver->name);\n    info->fix.type =           FB_TYPE_PACKED_PIXELS;\n    info->fix.visual =         FB_VISUAL_TRUECOLOR;\n    info->fix.xpanstep =       0;\n    \/\/...\u5927\u90e8\u5206\u521d\u59cb\u5316\u53c2\u6570\u7701\u7565<\/code><\/pre>\n<h3>3.2.3 \u914d\u7f6e\u989c\u8272\u76f8\u5173\u7ed3\u6784\u4f53\u4e0e\u51fd\u6570<\/h3>\n<p>&emsp;&emsp;\u914d\u7f6e\u989c\u8272\u6d89\u53ca\u5230\u4e86\u6838\u5fc3\u7684fbtft_framebuffer_alloc\u51fd\u6570\uff0c\u901a\u8fc7\u5bf9\u6b64\u51fd\u6570\u8c03\u7528\u6d41\u7a0b\u7684\u5206\u6790\uff0c\u4e86\u89e3\u7cfb\u7edf\u5bf9\u989c\u8272\u6570\u636e\u7684\u5904\u7406\u64cd\u4f5c\u3002<\/p>\n<h4>3.2.3.1 \u521d\u59cb\u5316\u7ed3\u6784\u4f53\u51fd\u6570\u6307\u9488<\/h4>\n<pre><code class=\"language-c\">\/\/\u5728fbtft_framebuffer_alloc\u51fd\u6570\u4e2d\u521d\u59cb\u5316\u51fd\u6570\u6307\u9488\n    fbops->fb_setcolreg =      fbtft_fb_setcolreg;\n\/\/\u914d\u7f6e\u989c\u8272\u7684\u6570\u636e\u7c7b\u578b\n    \/* RGB565 *\/\/\/\u7ea2\u84dd\u53cd\u4e86\n    info->var.red.offset =     11;\n    info->var.red.length =     5;\n    info->var.green.offset =   5;\n    info->var.green.length =   6;\n    info->var.blue.offset =    0;\n    info->var.blue.length =    5;\n    info->var.transp.offset =  0;\n    info->var.transp.length =  0;\n    info->flags =              FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB;<\/code><\/pre>\n<h4>3.2.3.2 \u8bbe\u7f6e\u989c\u8272\u5bc4\u5b58\u5668(\u8c03\u8272\u677f)\u7684\u51fd\u6570<\/h4>\n<p>&emsp;&emsp;\u5230\u8fd9\u5176\u5b9e\u57fa\u672c\u5c31\u53ef\u4ee5\u505c\u6b62\u5206\u6790\u4e86\uff0c\u56e0\u4e3a\u5206\u6790\u5230\u8fd9\u6211\u53d1\u73b0\u9a71\u52a8\u538b\u6839\u5c31\u6ca1\u4f7f\u7528\u8fd9\u4e2a\u663e\u793a\u989c\u8272\u7684\u65b9\u5f0f\u3002\u4ece\u4e0b\u9762\u4ee3\u7801\u53ef\u4ee5\u770b\u51fa\uff0cpal[regno]\u5728\u8fd9\u8d4b\u503c\u540e\uff0c\u538b\u6839\u5c31\u6ca1\u6709\u51fd\u6570\u8c03\u7528\u8fd9\u4e2a\u63a5\u53e3\u3002\u9a71\u52a8\u662f\u76f4\u63a5\u4f7f\u7528\u7684&quot;rgb565&quot;16\u4f4d\u7684\u989c\u8272\u6570\u636e\u3002<\/p>\n<pre><code class=\"language-c\">\/\/\u8bbe\u7f6e\u989c\u8272\u5bc4\u5b58\u5668\u7684\u51fd\u6570\nstatic int fbtft_fb_setcolreg(unsigned int regno, unsigned int red,\n                  unsigned int green, unsigned int blue,\n                  unsigned int transp, struct fb_info *info)\n{\n    unsigned int val;\n    int ret = 1;\n\n    dev_dbg(info->dev,\n        \"%s(regno=%u, red=0x%X, green=0x%X, blue=0x%X, trans=0x%X)\\n\",\n        __func__, regno, red, green, blue, transp);\n\n    switch (info->fix.visual) {\n    case FB_VISUAL_TRUECOLOR:\n    \/\/\u914d\u7f6e\u7684\u57fa\u7840\u8272\u4e00\u517116\u4e2a\n        if (regno < 16) {\n            u32 *pal = info->pseudo_palette;\n\n            val  = chan_to_field(red,   &info->var.red);\n            val |= chan_to_field(green, &info->var.green);\n            val |= chan_to_field(blue,  &info->var.blue);\n            \/\/\u6839\u636e\u503c\u786e\u5b9a\u6700\u540e\u7684\u989c\u8272\n            pal[regno] = val;\n            ret = 0;\n        }\n        break;\n    }\n    return ret;\n}<\/code><\/pre>\n<p>&emsp;&emsp;\u5c06\u901a\u9053\u503c\u8f6c\u6362\u6210\u4e00\u4e2a\u5e27\u7f13\u51b2\u768416\u4f4d\u5b57\u6bb5\u503c<\/p>\n<pre><code class=\"language-c\">\/* from pxafb.c *\/\n\/\/\u5c06\u901a\u9053\u503c\u8f6c\u6362\u6210\u4e00\u4e2a\u5e27\u7f13\u51b2\u768416\u4f4d\u5b57\u6bb5\u503c\nstatic unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)\n{\n    chan &= 0xffff;\n    chan >>= 16 - bf->length;\n    return chan << bf->offset;\n}<\/code><\/pre>\n<h4>3.2.3.3 8\u4f4d\u771f\u5f69\u7684\u663e\u793a\u903b\u8f91<\/h4>\n<p>&emsp;&emsp;\u5047\u8bbe\u9a71\u52a8\u4f7f\u7528\u6b64\u65b9\u5f0f\u663e\u793a\u989c\u8272\uff0c\u7ee7\u7eed\u5f80\u4e0b\u5206\u6790\u3002\u56e0\u6b64\u9700\u8981\u5148\u4e86\u89e38\u4f4d\u771f\u5f69\u4ee5\u53ca\u8c03\u8272\u677f\u7684\u6982\u5ff5\u3002<br \/>\n&emsp;&emsp;\u8c03\u8272\u677f\u7684\u6982\u5ff5\uff1a\u5047\u5982LCD\u7684\u6570\u636e\u4f4d\u4e3a16\u4f4d\uff0c\u90a3\u4e48framebuffer\u5e94\u8be5\u6bcf\u4e2a\u50cf\u7d20\u5360\u636e16bit\uff0c\u4f46\u662f\u4e3a\u4e86\u8282\u7701\u7a7a\u95f4\uff0c\u4f7f\u75288bit\u8868\u793a\u4e00\u4e2a\u50cf\u7d20\uff0c\u8fd9\u65f6\u5019\u9700\u8981\u5f15\u5165\u8c03\u8272\u677f\u624d\u80fd\u6b63\u786e\u4f20\u8f9316\u4f4d\u7684\u6570\u636e\u7ed9LCD\uff08\u6b63\u786e\u4f20\u8f93\u6bcf\u4e2a\u50cf\u7d20\u7684\u6570\u636e\uff09\u3002\u8c03\u8272\u677f\u5176\u5b9e\u5c31\u662f\u4e00\u7247\u5185\u5b58\uff0c\u8fd9\u91cc\u9762\u6bcf\u4e00\u683c\u5b58\u653e16bit\u7684\u6570\u636e\u3002\u5f53LCD\u63a7\u5236\u5668\u4eceframebuffer\u4e2d\u53d6\u51fa8bit\u7684\u6570\u636e\u540e\uff0c\u4e0d\u662f\u76f4\u63a5\u4f20\u7ed9LCD\uff0c\u800c\u662f\u7528\u8fd9\u4e2a8bit\u4f5c\u4e3a\u7d22\u5f15\uff0c\u4ece\u8c03\u8272\u677f\u4e2d\u53d6\u51fa16bit\u7684\u6570\u636e\uff0c\u7136\u540e\u53d1\u7ed9LCD\u3002\u6240\u4ee5\uff0c\u5728\u4f7f\u75288BPP\uff08\u6bcf\u4e2a\u50cf\u7d20\u70b9\u7684\u4f4d\u6570\uff09\u683c\u5f0f\u65f6\uff0cframebuffer\u4e2d\u5b58\u653e\u7684\u662f\u4f2a\u5f69\u8272\u300216BPP\u6216\u800524BPP\u683c\u5f0f\u65f6\uff0cframebuffer\u4e2d\u5b58\u653e\u7684\u624d\u662f\u771f\u5f69\u8272\u3002\u6240\u4ee5\u5728\u4f7f\u75288BPP\u683c\u5f0f\u65f6\uff0c\u9996\u5148\u8981\u8bbe\u7f6e\u8c03\u8272\u677f\u3002\u5982\u4e0b\u56fe\u662f\u8c03\u7528\u903b\u8f91\uff1a<\/p>\n<p><center><br \/>\n<div class='fancybox-wrapper lazyload-container-unload' data-fancybox='post-images' href='https:\/\/www.yanwenkai.com:7777\/images\/2024\/09\/10\/2024-09-03-12.png'><img class=\"lazyload lazyload-style-1\" src=\"data:image\/svg+xml;base64,PCEtLUFyZ29uTG9hZGluZy0tPgo8c3ZnIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgc3Ryb2tlPSIjZmZmZmZmMDAiPjxnPjwvZz4KPC9zdmc+\"  loading=\"lazy\" decoding=\"async\" data-original=\"https:\/\/www.yanwenkai.com:7777\/images\/2024\/09\/10\/2024-09-03-12.png\" src=\"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB\/AAffA0nNPuCLAAAAAElFTkSuQmCC\" width=\"600\" height=\"400\"><\/div><br \/>\n<\/center><\/p>\n<p>&emsp;&emsp;\u7efc\u4e0a\u6240\u8ff0\u6211\u4eec\u4e86\u89e3\u4e86\u6574\u4e2a8\u4f4d\u771f\u5f69\u663e\u793a\u7684\u8c03\u7528\u6d41\u7a0b\uff0c\u9a71\u52a8\u4ee3\u7801\u91cc\u7f3a\u5931\u7684\u90e8\u5206\u5c31\u662f\u4ece\u5e94\u7528\u5c42\u83b7\u53d68\u4f4d\u7684\u989c\u8272\u7f16\u7801\u540e\uff0c\u901a\u8fc7\u7f16\u7801\u8c03\u7528\u8c03\u8272\u677f\u7684\u903b\u8f91\uff0c\u56e0\u4e3a\u6ca1\u7528\u8fc7\u5c31\u6ca1\u5199\u8fd9\u4e2a\u903b\u8f91\uff0c\u5927\u6982\u4e86\u89e3\u5c31\u884c\u4e86\u3002<\/p>\n<h4>3.2.3.4 \u5e94\u7528\u5c42\u8c03\u7528\u8c03\u8272\u677f\u793a\u4f8b<\/h4>\n<p>&emsp;&emsp;\u5982\u4e0b\u4ee3\u7801\u662f\u7b80\u5355\u7684\u8c03\u7528ioctl\u63a5\u53e3\u5b9e\u73b08\u4f4d\u771f\u5f69\u663e\u793a\u989c\u8272\u7684\u793a\u4f8b\uff1b\u5982\u679c\u60f3\u7528mmap\u7684\u63a5\u53e3\uff0c\u53ea\u9700\u628a\u989c\u8272\u6570\u636e\u5904\u7406\u90e8\u5206\u653e\u5230\u9a71\u52a8\u4e2d\uff0c\u5e94\u7528\u5c42\u4e0e\u9a71\u52a8\u5c42\u914d\u7f6e\u597d\u4f20\u8f93\u534f\u8bae\u53ef\u4ee5\u4e86\u3002<\/p>\n<pre><code class=\"language-c\">#include &ltlinux\/fb.h&gt\n#include &ltfcntl.h&gt\n#include &ltunistd.h&gt\n#include &ltsys\/ioctl.h&gt\n\nint set_color(int fb_fd, unsigned int regno, unsigned int red, unsigned int green, unsigned int blue) {\n    struct fb_cmap cmap;\n    cmap.start = regno;           \/\/ \u8bbe\u7f6e\u989c\u8272\u5bc4\u5b58\u5668\u53f7\n    cmap.len = 1;                 \/\/ \u53ea\u8bbe\u7f6e\u4e00\u4e2a\u989c\u8272\n    cmap.red = &red;              \/\/ \u7ea2\u8272\u503c\n    cmap.green = &green;          \/\/ \u7eff\u8272\u503c\n    cmap.blue = &blue;            \/\/ \u84dd\u8272\u503c\n    cmap.transp = NULL;           \/\/ \u4e0d\u900f\u660e\u5ea6\n\n    return ioctl(fb_fd, FBIOPUTCMAP, &cmap);\n}\n\nint main() {\n    int fb_fd = open(\"\/dev\/fb0\", O_RDWR);\n    if (fb_fd < 0) {\n        return -1;\n    }\n\n    \/\/ \u8bbe\u7f6e\u5bc4\u5b58\u5668 0 \u5bf9\u5e94\u7684\u989c\u8272\u4e3a\u7ea2\u8272\n    set_color(fb_fd, 0, 0xFFFF, 0x0000, 0x0000);\n\n    close(fb_fd);\n    return 0;\n}<\/code><\/pre>\n<h4>3.2.3.5 \u5b9e\u9645\u989c\u8272\u663e\u793a\u65b9\u5f0f<\/h4>\n<p>&emsp;&emsp;\u800c\u5728\u672c\u9a71\u52a8\u4e2d\u5e76\u672a\u4f7f\u7528\u4e0a\u6587\u663e\u793a\u989c\u8272\u7684\u903b\u8f91\uff1b\u56e0\u4e3a\u4f7f\u7528\u7684\u662f16\u4f4drgb565\u989c\u8272\u6570\u636e\uff0cframmebuffer\u76f4\u63a5\u63a5\u6536\u989c\u8272\u6570\u636e\u53d1\u9001\u7ed9\u5c4f\u5e55\u5373\u53ef\uff0c\u4e0d\u9700\u8981\u50cf\u8fd9\u79cd8\u4f4d\u771f\u5f69\u4e00\u6837\u8fdb\u884c\u6570\u636e\u5904\u7406\u3002\u5b9e\u9645\u7684\u663e\u793a\u989c\u8272\u7684\u65b9\u5f0f\u770b\u4e0a\u6587mmap\u51fd\u6570\u7684\u6574\u4f53\u8c03\u7528\u6d41\u7a0b\u3002<\/p>\n<h2>3.3 framebuffer\u4e2d\u7684spi\u5b50\u7cfb\u7edf<\/h2>\n<p>&emsp;&emsp;\u9a71\u52a8\u4e0d\u8bba\u600e\u4e48\u5199\uff0c\u4f46\u51e1\u6d89\u53ca\u5230spi\u63a5\u53e3\u7684\u6570\u636e\u4f20\u8f93\uff0c\u80af\u5b9a\u4f1a\u4f7f\u7528spi\u5b50\u7cfb\u7edf\u3002\u552f\u4e00\u7684\u533a\u522b\u5728\u4e8e\u5f00\u53d1\u4eba\u5458\u4f1a\u56e0\u52bf\u5229\u5bfc\u5bf9\u5b50\u7cfb\u7edf\u4ee3\u7801\u7684\u8c03\u7528\u505a\u66f4\u5408\u9002\u672c\u9a71\u52a8\u7684\u4f18\u5316\u7f62\u4e86\uff01<\/p>\n<h3>3.3.1 \u521d\u59cb\u5316\u9a71\u52a8\u81ea\u5e26\u7684\u7ed3\u6784\u4f53<\/h3>\n<p>&emsp;&emsp;\u901a\u8fc7prob\u51fd\u6570\u521d\u59cb\u5316fbtft_framebuffer_alloc\u51fd\u6570\u4e2d\u7684par\u9a71\u52a8\u79c1\u6709\u7ed3\u6784\u4f53\u3002<\/p>\n<pre><code class=\"language-c\">\/\/\u521d\u59cb\u5316par\u7ed3\u6784\u4f53\u7684\u5404\u9879\u53c2\u6570\uff0c\u5982\u8c03\u8bd5\u6807\u5fd7\uff0c\u7f13\u51b2\u533a\u6307\u9488\uff0c\u65cb\u8f6c\u89d2\u5ea6\uff0cgamma\u66f2\u7ebf\n    par = info->par;\n    par->info = info;\n    par->pdata = pdata;\n    par->debug = display->debug;\n    par->buf = buf;\n    spin_lock_init(&par->dirty_lock);\n    \/\/par->bgr = pdata->bgr;\n    par->bgr = 1;\/\/\u7ea2\u84dd\u53cd\u7f6e\u53c2\u6570\n    par->fbtftops.write = fbtft_write_spi;\/\/spi\u5b50\u7cfb\u7edf\u7684\u5199\u51fd\u6570\n    par->fbtftops.write_vmem = fbtft_write_vmem16_bus8;\/\/spi\u5b50\u7cfb\u7edf\u5199\u51fd\u6570\u7684\u8c03\u7528\u51fd\u6570\n    \/\/...\u5927\u90e8\u5206\u521d\u59cb\u5316\u53c2\u6570\u7701\u7565<\/code><\/pre>\n<h3>3.3.2 \u5b9e\u9645\u521d\u59cb\u5316\u903b\u8f91<\/h3>\n<p>&emsp;&emsp;\u5206\u6790fbtft_write_vmem16_bus8\u51fd\u6570\uff1b\u5728\u4e0a\u8ff0\u7ed3\u6784\u4f53\u4e2d\u867d\u7136\u8d4b\u503c\u4e86\uff0c\u4f46\u5728prob\u51fd\u6570\u4e2d\u53c8\u91cd\u65b0\u8fdb\u884c\u4e86\u8d4b\u503c\u5224\u65ad\uff0c\u6240\u4ee5\u4e0a\u9762\u7684\u8d4b\u503c\u65e0\u6548\u3002\u771f\u6b63\u7684\u8d4b\u503c\u903b\u8f91\u5982\u4e0b\u6240\u793a\uff0c\u9700\u8981\u5224\u65ad\u603b\u7ebf\u5bbd\u5ea6\u7684\u503c\uff0c\u6765\u786e\u5b9a\u4f20\u8f93\u51fd\u6570\u5230\u5e95\u662f\u54ea\u4e2a\uff1f<\/p>\n<pre><code class=\"language-c\">    if (display->buswidth == 8)\n        par->fbtftops.write_vmem = fbtft_write_vmem16_bus8;\n    else if (display->buswidth == 9)\n        par->fbtftops.write_vmem = fbtft_write_vmem16_bus9;\n    else if (display->buswidth == 16)\n        par->fbtftops.write_vmem = fbtft_write_vmem16_bus16;<\/code><\/pre>\n<h3>3.3.3 buildwith\u7684\u8d4b\u503c<\/h3>\n<pre><code class=\"language-c\">\/\/\u4ece\u8bbe\u5907\u7684\u5c5e\u6027\u4e2d\u8bfb\u53d6\u4fe1\u606f\uff0c\u586b\u5145\u5230fbtft_platform_data\u7ed3\u6784\u4f53\u4e2d\nstatic struct fbtft_platform_data *fbtft_properties_read(struct device *dev)\n{\n    struct fbtft_platform_data *pdata;\n    pdata->display.buswidth = fbtft_property_value(dev, \"buswidth\");\n    pdata->bgr = device_property_read_bool(dev, \"bgr\");<\/code><\/pre>\n<h3>3.3.4 \u8bbe\u5907\u6811\u7684\u914d\u7f6e<\/h3>\n<p>&emsp;&emsp;\u914d\u7f6e\u8bbe\u5907\u6811\u4e2d\u7684buswidth\u5c5e\u6027\u503c\u3002<\/p>\n<pre><code class=\"language-c\">   ili9488@0{\n       compatible = \"ilitek,ili9488\";\n       buswidth = <8>;\n       bgr = <1>;\n   };<\/code><\/pre>\n<h3>3.3.5 \u8c03\u7528fbtft_write_vmem16_bus8\u7684\u6d41\u7a0b<\/h3>\n<p>&emsp;&emsp;\u5728\u786e\u5b9a\u8fd9\u4e2a\u662f\u5199\u51fd\u6570\u540e\uff0c\u90a3\u5c31\u8981\u786e\u5b9a\u5176\u8c03\u7528\u6d41\u7a0b\uff0c\u9010\u5c42\u5206\u6790\u8c03\u7528\u4ee3\u7801\u903b\u8f91\u3002\u5177\u4f53\u8c03\u7528\u6d41\u7a0b\u5982\u4e0b\uff1a<br \/>\n1.\u5c06\u5199\u51fd\u6570\u96c6\u6210\u8fdb\u5237\u65b0\u5c4f\u5e55\u7684\u51fd\u6570\u4e2d\uff0c\u5c06\u591a\u4e2a\u51fd\u6570\u62bd\u8c61\u4e3a\u5237\u65b0\u5c4f\u5e55\u529f\u80fd\u3002<\/p>\n<pre><code class=\"language-c\">\/\/\u66f4\u65b0\u663e\u793a\u5c4f\u7684\u5185\u5bb9\uff0c\u6839\u636e\u8d77\u59cb\u884c\u548c\u7ed3\u675f\u884c\u7684\uff0c\u5c06\u5bf9\u5e94\u7684\u663e\u5b58\u5185\u5bb9\u5199\u5165\u5230\u663e\u793a\u5c4f\nstatic void fbtft_update_display(struct fbtft_par *par, unsigned int start_line,\n                 unsigned int end_line)\n{\n    ret = par->fbtftops.write_vmem(par, offset, len);<\/code><\/pre>\n<p>2.\u4e3a\u7ed3\u6784\u4f53\u4e2d\u7684\u51fd\u6570\u6307\u9488\u8d4b\u503c\u3002<\/p>\n<pre><code class=\"language-c\">par->fbtftops.update_display = fbtft_update_display;<\/code><\/pre>\n<p>3.\u6ce8\u518c\u9636\u6bb5\u8c03\u7528\u66f4\u65b0\u5c4f\u5e55\u51fd\u6570<br \/>\n&emsp;&emsp;\u6ce8\u518c\u9636\u6bb5\u9700\u8981\u8c03\u7528\u5199\u51fd\u6570\u4f20\u8f93\u521d\u59cb\u5316\u547d\u4ee4\u7b49\u64cd\u4f5c\u3002\u5728register\u4e2d\u8c03\u7528\u66f4\u65b0\u5c4f\u5e55\u7684\u903b\u8f91\u95f4\u63a5\u8c03\u7528\u5199\u51fd\u6570\u3002<\/p>\n<pre><code class=\"language-c\">int fbtft_register_framebuffer(struct fb_info *fb_info)\n{\npar->fbtftops.update_display(par, 0, par->info->var.yres - 1);<\/code><\/pre>\n<p>&emsp;&emsp;\u6700\u540e\u7531prob\u51fd\u6570\u4e2d\u8c03\u7528\u6ce8\u518c\u51fd\u6570\uff0c\u5411\u5199\u51fd\u6570\u4e2d\u4f20\u8f93\u6570\u636e\u3002<\/p>\n<pre><code class=\"language-c\">int fbtft_probe_common(struct fbtft_display *display,\n               struct spi_device *sdev,\n               struct platform_device *pdev)\n{\nret = fbtft_register_framebuffer(info);<\/code><\/pre>\n<p>5.\u6b63\u5e38\u5237\u65b0\u5c4f\u5e55<br \/>\n&emsp;&emsp;\u9664\u4e86\u6ce8\u518c\u9636\u6bb5\u8981\u5199\u6570\u636e\uff0c\u5237\u65b0\u5c4f\u5e55\u4e5f\u8981\u5199\u6570\u636e\u3002\u6b64\u90e8\u5206\u9a71\u52a8\u5730\u601d\u8def\u662f\u4f7f\u7528\u5b9a\u65f6\u5668\u56fa\u5b9a\u65f6\u95f4\u6b65\u957f\u8c03\u7528\u5237\u65b0\u51fd\u6570\u4f20\u8f93\u6570\u636e\u3002\u5177\u4f53\u5730\u903b\u8f91\u5982\u4e0b\uff1a<\/p>\n<pre><code class=\"language-c\">\/\/\u5ef6\u65f6io\u51fd\u6570\nstatic void fbtft_deferred_io(struct fb_info *info, struct list_head *pagelist)\n{\n    par->fbtftops.update_display(info->par,\n                    dirty_lines_start, dirty_lines_end);<\/code><\/pre>\n<p>&emsp;&emsp;\u5c06\u9a71\u52a8\u7684\u5ef6\u65f6I\/O\u51fd\u6570\u586b\u5145\u5230\u51fd\u6570\u6307\u9488\u4e2d\uff1b\u5e76\u521d\u59cb\u5316\u5b9a\u65f6\u5668\u51fd\u6570\u3002<\/p>\n<pre><code class=\"language-c\">    fbdefio->deferred_io =     fbtft_deferred_io;\n    fb_deferred_io_init(info);<\/code><\/pre>\n<p>&emsp;&emsp;\u6b63\u5e38\u8ffd\u51fd\u6570\u5230\u8fd9\u91cc\u53c8\u65ad\u6389\u4e86\uff0c\u5206\u6790\u53d1\u73b0fb_deferred_io_init\u662fframmebuffer\u5b50\u7cfb\u7edf\u81ea\u5e26\u7684\u5ef6\u65f6I\/O\u673a\u5236\u51fd\u6570\u3002\u5185\u90e8\u4f7f\u7528\u5b9a\u65f6\u5668\u5b9e\u73b0\u4e86\u5b9a\u65f6\u8c03\u7528\u5199\u51fd\u6570\u7684\u903b\u8f91\uff0c\u5c06\u7f13\u51b2\u533a\u5185\u7684\u6570\u636e\u4f20\u8f93\u5230lcd\u5c4f\u5e55\u4e0a\u3002\u51fd\u6570\u7684\u5b9e\u73b0\u5728drivers\/video\/fbdev\/core\/fb_defio.c\u6587\u4ef6\u91cc\u3002<\/p>\n<h3>3.3.6 \u5904\u7406\u6570\u636e\u7684\u903b\u8f91<\/h3>\n<p>&emsp;&emsp;\u5206\u6790\u5230\u6b64\u5904\u53ef\u4ee5\u770b\u51fa\u9a71\u52a8\u8c03\u7528\u7684\u51fd\u6570\u662ffbtft_write_vmem16_bus8\u3002\u6574\u4f53\u601d\u8def\u5c31\u662f\u5c06\u6570\u636e\u5b58\u653e\u5230vmem16\u7684\u6570\u7ec4\u4e2d\uff0c\u7136\u540e\u5229\u7528\u7cfb\u7edf\u7684\u8f6c\u6362\u5b57\u8282\u5e8f\u7684\u63a5\u53e3\u8fdb\u884c\u6570\u636e\u5904\u7406\uff0c\u7136\u540e\u53d1\u7ed9\u4e0b\u9762\u7684spi\u5b50\u7cfb\u7edf\u3002<\/p>\n<pre><code class=\"language-c\">\/* 16 bit pixel over 8-bit databus *\/\nint fbtft_write_vmem16_bus8(struct fbtft_par *par, size_t offset, size_t len)\n{\n    u16 *vmem16;\n    __be16 *txbuf16 = par->txbuf.buf;\n    size_t remain;\n    size_t to_copy;\n    size_t tx_array_size;\n    int i;\n    int ret = 0;\n    size_t startbyte_size = 0;\n\n    fbtft_par_dbg(DEBUG_WRITE_VMEM, par, \"%s(offset=%zu, len=%zu)\\n\",\n              __func__, offset, len);\n\n    remain = len \/ 2;\n    vmem16 = (u16 *)(par->info->screen_buffer + offset);\n\n    gpiod_set_value(par->gpio.dc, 1);\n\n    \/* non buffered write *\/\n    if (!par->txbuf.buf)\n        return par->fbtftops.write(par, vmem16, len);\n\n    \/* buffered write *\/\n    tx_array_size = par->txbuf.len \/ 2;\n\n    if (par->startbyte) {\n        txbuf16 = par->txbuf.buf + 1;\n        tx_array_size -= 2;\n        *(u8 *)(par->txbuf.buf) = par->startbyte | 0x2;\n        startbyte_size = 1;\n    }\n\n    while (remain) {\n        to_copy = min(tx_array_size, remain);\n        dev_dbg(par->info->device, \"to_copy=%zu, remain=%zu\\n\",\n            to_copy, remain - to_copy);\n\n        for (i = 0; i < to_copy; i++)\n        {\n            txbuf16[i] = cpu_to_be16(vmem16[i]);\n\/\/            printk(\"\u8f6c\u6362\u5b57\u8282\u5e8faaaaaaaaa\\n\");\n\n        }\n\n        vmem16 = vmem16 + to_copy;\n        ret = par->fbtftops.write(par, par->txbuf.buf,\n                        startbyte_size + to_copy * 2);\n        if (ret < 0)\n            return ret;\n        remain -= to_copy;\n    }\n\n    return ret;\n}<\/code><\/pre>\n<h3>3.3.7 spi\u5b50\u7cfb\u7edf\u4f20\u8f93\u6570\u636e<\/h3>\n<p>&emsp;&emsp;\u8c03\u7528spi\u5b50\u7cfb\u7edfpar-&gt;fbtftops.write\u3002<\/p>\n<pre><code class=\"language-c\">int fbtft_write_spi(struct fbtft_par *par, void *buf, size_t len)\n{\n    struct spi_transfer t = {\n        .tx_buf = buf,\n        .len = len,\n    };\n    struct spi_message m;\n\n    fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len,\n              \"%s(len=%zu): \", __func__, len);\n\n    if (!par->spi) {\n        dev_err(par->info->device,\n            \"%s: par->spi is unexpectedly NULL\\n\", __func__);\n        return -1;\n    }\n    spi_message_init(&m);\n    spi_message_add_tail(&t, &m);\n    return spi_sync(par->spi, &m);\n}<\/code><\/pre>\n<p>&emsp;&emsp;\u9a71\u52a8\u4e2dspi\u5b50\u7cfb\u7edf\u7684\u8c03\u7528\u6d41\u7a0b\u5b8c\u6210\u3002<\/p>\n<h2>3.4 \u5c0f\u7ed3<\/h2>\n<p>&emsp;&emsp;\u5b8c\u6210\u8fd9\u4e2a\u9a71\u52a8\u540e\uff0c\u603b\u7ed3\u53d1\u73b0framebuffer\u9a71\u52a8\u5bf9\u6bd4\u539f\u6709spi\u9a71\u52a8\u7684\u4f18\u70b9\u548c\u6838\u5fc3\u5c31\u662f\u4f7f\u7528\u4e86fb_mmap\u51fd\u6570\u3002\u5b83\u662fframebuffer\u8bbe\u5907\u76f8\u5173\u7684\u4e00\u4e2a\u5185\u5b58\u6620\u5c04\u51fd\u6570\u3002\u5b83\u7684\u4e3b\u8981\u4f5c\u7528\u662f\u5c06framebuffer\u6216MMIOMemory-Mapped I\/O\uff09\u533a\u57df\u6620\u5c04\u5230\u7528\u6237\u7a7a\u95f4\uff0c\u4f7f\u7528\u6237\u53ef\u4ee5\u901a\u8fc7\u5185\u5b58\u8bbf\u95ee\u8bbe\u5907\u6570\u636e\u3002\u6700\u7ec8\u6548\u679c\u5c31\u662f\u63d0\u9ad8\u4e86\u5e94\u7528\u5c42\u548c\u5185\u6838\u6570\u636e\u7684\u4f20\u8f93\u6548\u7387\u3002<br \/>\n&emsp;&emsp;\u81f3\u6b64\u9a71\u52a8\u7684\u4e3b\u7ebf\u903b\u8f91\u5c31\u5df2\u7ecf\u634b\u6e05\u695a\u4e86\uff0c\u5176\u4ed6\u90e8\u5206\u4ee3\u7801\u903b\u8f91\u7b80\u5355\u6e05\u6670\uff0c\u6ca1\u5565\u8bb2\u89e3\u5730\u5fc5\u8981\u3002\u53e6\u5916\u6709\u4e00\u90e8\u5206\u521d\u59cb\u5316\u5730\u903b\u8f91\u5728\u786c\u4ef6\u7bc7\u5df2\u8bb2\u8fc7\uff0c\u9a71\u52a8\u7bc7\u5c31\u4e0d\u518d\u8d58\u8ff0\u4e86\uff01<br \/>\n\u5982\u4e0b\u662f\u6211\u6700\u7ec8\u9002\u914d\u5b8c\u6210\u7684\u9a71\u52a8\uff1a<\/p>\n<pre><code class=\"language-c\">\/\/ SPDX-License-Identifier: GPL-2.0+\n\/*\n * FB driver for the ST7789V LCD Controller\n *\n * Copyright (C) 2015 Dennis Menschel\n *\/\n\n#include < linux\/bitops.h >\n#include < linux\/delay.h >\n#include < linux\/init.h >\n#include < linux\/kernel.h >\n#include < linux\/module.h >\n#include < video\/mipi_display.h >\n\n#include < linux\/export.h >\n#include < linux\/errno.h >\n#include < linux\/gpio\/consumer.h >\n#include < linux\/spi\/spi.h >\n\n#include < linux\/string.h >\n#include < linux\/mm.h >\n#include < linux\/vmalloc.h >\n#include < linux\/slab.h >\n#include < linux\/fb.h >\n#include < linux\/uaccess.h >\n#include < linux\/backlight.h >\n#include < linux\/platform_device.h >\n#include < linux\/property.h >\n#include < linux\/spinlock.h >\n\n#include < linux\/gpio.h > \n#include < linux\/of_gpio.h > \n\n#define DRVNAME \"ili9488\"\n\n#define FBTFT_GPIO_NAME_SIZE 32\n\nstruct fbtft_par; \/\/\u9884\u5148\u58f0\u660e\uff0c\u5426\u5219\u4e0d\u8bc6\u522b\u7ed3\u6784\u4f53\nstruct fbtft_gpio;\n\n\/*********************************************************\u9a71\u52a8\u6240\u9700\u7ed3\u6784\u4f53*************************************************************************************\/\n\n\/**\n * struct fbtft_ops - FBTFT operations structure  fbtft\u64cd\u4f5c\u7ed3\u6784\n * @write: Writes to interface bus  \u5199\u5230\u63a5\u53e3\u603b\u7ebf\n * @read: Reads from interface bus  \u4ece\u63a5\u53e3\u603b\u7ebf\u8bfb\u53d6\n * @write_vmem: Writes video memory to display  \u5199\u663e\u5b58\u5230\u663e\u793a\u5668\n * @write_reg: Writes to controller register  \u5199\u5165\u63a7\u5236\u5668\u5bc4\u5b58\u5668\n * @set_addr_win: Set the GRAM update window  \u8bbe\u7f6eGRAM\u66f4\u65b0\u7a97\u53e3\n * @reset: Reset the LCD controller   \u91cd\u7f6elcd\u63a7\u5236\u5668\n * @mkdirty: Marks display lines for update  \u6807\u8bb0\u663e\u793a\u884c\u66f4\u65b0\n * @update_display: Updates the display  \u66f4\u65b0\u663e\u793a \n * @init_display: Initializes the display  \u521d\u59cb\u5316\u663e\u793a\n * @blank: Blank the display (optional)  \u6e05\u7a7a\u663e\u793a\uff08\u53ef\u9009\uff09\n * @request_gpios_match: Do pinname to gpio matching  \u505apinname\u5230gpio\u5339\u914d\n * @request_gpios: Request gpios from the kernel  \u4ece\u5185\u6838\u8bf7\u6c42gpios\n * @free_gpios: Free previously requested gpios  \u91ca\u653e\u4ee5\u524d\u8bf7\u6c42\u5f97gpio\n * @verify_gpios: Verify that necessary gpios is present (optional)  \u9a8c\u8bc1\u662f\u5426\u5b58\u5728\u5fc5\u8981\u5f97gpios\uff08\u53ef\u9009\uff09\n * @register_backlight: Used to register backlight device (optional)  \u7528\u4e8e\u6ce8\u518c\u80cc\u5149\u8bbe\u5907\uff08\u53ef\u9009\uff09\n * @unregister_backlight: Unregister backlight device (optional) \u6ce8\u9500\u80cc\u5149\u8bbe\u5907\uff08\u53ef\u9009\uff09\n * @set_var: Configure LCD with values from variables like @rotate and @bgr \u7528rotate\u548cbgr\u7b49\u53d8\u91cf\u503c\u8bbe\u7f6elcd\n *           (optional)\n * @set_gamma: Set Gamma curve (optional) \u8bbe\u7f6egamma\u66f2\u7ebf\uff08\u53ef\u9009\uff09\n *\n * Most of these operations have default functions assigned to them in  \u8fd9\u4e9b\u64cd\u4f5c\u4e2d\u5f97\u5927\u591a\u6570\u90fd\u6709\u4e3a\u5176\u5206\u914d\u5f97\u9ed8\u8ba4\u51fd\u6570\n *     fbtft_framebuffer_alloc()\n *\/\nstruct fbtft_ops {\n    int (*write)(struct fbtft_par *par, void *buf, size_t len);\n    int (*read)(struct fbtft_par *par, void *buf, size_t len);\n    int (*write_vmem)(struct fbtft_par *par, size_t offset, size_t len);\n    void (*write_register)(struct fbtft_par *par, int len, ...);\n\n    void (*set_addr_win)(struct fbtft_par *par,\n                 int xs, int ys, int xe, int ye);\n    void (*reset)(struct fbtft_par *par);\n    void (*mkdirty)(struct fb_info *info, int from, int to);\n    void (*update_display)(struct fbtft_par *par,\n                   unsigned int start_line, unsigned int end_line);\n    int (*init_display)(struct fbtft_par *par);\n    int (*blank)(struct fbtft_par *par, bool on);\n\n    unsigned long (*request_gpios_match)(struct fbtft_par *par,\n                         const struct fbtft_gpio *gpio);\n    int (*request_gpios)(struct fbtft_par *par);\n    int (*verify_gpios)(struct fbtft_par *par);\n\n    void (*register_backlight)(struct fbtft_par *par);\n    void (*unregister_backlight)(struct fbtft_par *par);\n\n    int (*set_var)(struct fbtft_par *par);\n    int (*set_gamma)(struct fbtft_par *par, u32 *curves);\n};\n\n\/**\n * struct fbtft_display - Describes the display properties \u63cf\u8ff0\u663e\u793a\u5c5e\u6027\n * @width: Width of display in pixels \u663e\u793a\u5bbd\u5ea6\uff08\u4ee5\u50cf\u7d20\u4e3a\u5355\u4f4d\uff09\n * @height: Height of display in pixels \u663e\u793a\u9ad8\u5ea6\uff08\u4ee5\u50cf\u7d20\u4e3a\u5355\u4f4d\uff09\n * @regwidth: LCD Controller Register width in bits lcd\u63a7\u5236\u5bc4\u5b58\u5668\u5bbd\u5ea6\n * @buswidth: Display interface bus width in bits \u663e\u793a\u63a5\u53e3\u603b\u7ebf\u5bbd\u5ea6\n * @backlight: Backlight type. \u80cc\u5149\u7c7b\u578b\n * @fbtftops: FBTFT operations provided by driver or device (platform_data) \u9a71\u52a8\u7a0b\u5e8f\u6216\u8bbe\u5907\u63d0\u4f9b\u7684FBTFT\u64cd\u4f5c\uff08platform_data\uff09\n * @bpp: Bits per pixel \u6bd4\u7279\u6bcf\u50cf\u7d20\n * @fps: Frames per second \u6bcf\u79d2\u5e27\u6570\n * @txbuflen: Size of transmit buffer \u4f20\u8f93\u7f13\u51b2\u533a\u7684\u5927\u5c0f\n * @init_sequence: Pointer to LCD initialization array \u6307\u5411lcd\u521d\u59cb\u5316\u6570\u7ec4\u7684\u6307\u9488\n * @gamma: String representation of Gamma curve(s) gamma\u66f2\u7ebf\u7684\u5b57\u7b26\u4e32\u8868\u793a\u5f62\u5f0f\n * @gamma_num: Number of Gamma curves gamma\u66f2\u7ebf\u7684\u4e2a\u6570\n * @gamma_len: Number of values per Gamma curve \u6bcf\u4e2a\u4f3d\u9a6c\u66f2\u7ebf\u7684\u503c\u7684\u6570\u91cf\n * @debug: Initial debug value  \u521d\u59cb\u8c03\u8bd5\u503c\n *\n * This structure is not stored by FBTFT except for init_sequence. \u8fd9\u4e2a\u7ed3\u6784\u4f53\u4e0d\u88abfbtft\u50a8\u5b58\n *\/\nstruct fbtft_display {\n    unsigned int width;\n    unsigned int height;\n    unsigned int regwidth;\n    unsigned int buswidth;\n    unsigned int backlight;\n    struct fbtft_ops fbtftops;\n    unsigned int bpp;\n    unsigned int fps;\n    int txbuflen;\n    const s16 *init_sequence;\n    char *gamma;\n    int gamma_num;\n    int gamma_len;\n    unsigned long debug;\n};\n\n\/**\n * struct fbtft_platform_data - \u5411\u9a71\u52a8\u7a0b\u5e8f\u4f20\u9012\u7279\u5b9a\u7684\u663e\u793a\u6570\u636e\n * @display: \u663e\u793a\u5c5e\u6027\n * @gpios:  \u6307\u5411gpio\u6620\u5c04\u7684pinname\u6570\u7ec4\u7684\u6307\u9488\n * @rotate: \u663e\u793a\u65cb\u8f6c\u89d2\u5ea6\n * @bgr:  lcd\u63a7\u5236\u7684bgr\u4f4d\n * @fps: \u6bcf\u79d2\u5e27\u7387\u6570\uff08\u8fd9\u5c06\u6d88\u5931\uff0c\u5728@fbtft_display\u4e2d\u4f7f\u7528@fps\uff09\n * @txbuflen: \u4f20\u8f93\u7f13\u51b2\u533a\u7684\u5927\u5c0f\n * @startbyte: \u5f53\u88ab\u8bbe\u7f6e\u65f6\uff0c\u542f\u7528\u5728\u4f20\u8f93\u4e2d\u4f7f\u7528Startbyte\n * @gamma:gamma\u66f2\u7ebf\u7684\u5b57\u7b26\u4e32\u8868\u8ff0\u5f62\u5f0f\n * @extra:\u4f20\u9012\u989d\u5916\u4fe1\u606f\u7684\u65b9\u6cd5\n *\/\nstruct fbtft_platform_data {\n    struct fbtft_display display;\n    unsigned int rotate;\n    bool bgr;\n    unsigned int fps;\n    int txbuflen;\n    u8 startbyte;\n    char *gamma;\n    void *extra;\n};\n\n\/**\n * struct fbtft_par - Main FBTFT data structure  \u4e3bfbtft\u6570\u636e\u7ed3\u6784\n *\n * This structure holds all relevant data to operate the display \u8be5\u7ed3\u6784\u4fdd\u5b58\u4e86\u64cd\u4f5c\u663e\u793a\u5668\u6240\u9700\u7684\u6240\u6709\u76f8\u5173\u6570\u636e\n *\n * See sourcefile for documentation since nested structs is not  kernel-doc\u4e0d\u652f\u6301\u5d4c\u5957\u7ed3\u6784\uff0c\u53c2\u4e0e\u6e90\u6587\u4ef6\u83b7\u53d6\u6587\u6863\n * supported by kernel-doc.\n *\n *\/\n\/* @spi: Set if it is a SPI device \u5f53\u662fspi\u8bbe\u5907\u65f6\u8bbe\u7f6e\n * @pdev: Set if it is a platform device  \u5f53\u662f\u5e73\u53f0\u8bbe\u5907\u662f\u7684\u8bbe\u7f6e\n * @info: Pointer to framebuffer fb_info structure fb_info\u7ed3\u6784\u7684\u6307\u9488\n * @pdata: Pointer to platform data \u6307\u5411\u5e73\u53f0\u6570\u636e\u7684\u6307\u9488\n * @ssbuf: Not used \u6ca1\u6709\u4f7f\u7528\n * @pseudo_palette: Used by fb_set_colreg() \u7531fb_set_colreg()\u4f7f\u7528\n * @txbuf.buf: Transmit buffer \u53d1\u9001\u7f13\u51b2\u533a\n * @txbuf.len: Transmit buffer length \u53d1\u9001\u7f13\u51b2\u533a\u957f\u5ea6\n * @buf: Small buffer used when writing init data over SPI \u5728spi\u4e0a\u5199init\u6570\u636e\u65f6\u4f7f\u7528\u7684\u5c0f\u7f13\u51b2\u533a\n * @startbyte: Used by some controllers when in SPI mode. \u5728spi\u6a21\u5f0f\u4e0b\u7531\u4e00\u4e9b\u63a7\u5236\u5668\u4f7f\u7528\n *             Format: 6 bit Device id + RS bit + RW bit \u6570\u636e\u683c\u5f0f\n * @fbtftops: FBTFT operations provided by driver or device (platform_data) \u9a71\u52a8\u7a0b\u5e8f\u6216\u8bbe\u5907\u63d0\u4f9b\u7684fbtft\u64cd\u4f5c\uff08plaplatform_data\uff09\n * @dirty_lock: Protects dirty_lines_start and dirty_lines_end \u4fdd\u62a4irty_lines_start \u548c dirty_lines_end\u7684\u9501\n * @dirty_lines_start: Where to begin updating display \u4ece\u54ea\u91cc\u5f00\u59cb\u66f4\u65b0\u663e\u793a\n * @dirty_lines_end: Where to end updating display \u5728\u54ea\u91cc\u7ed3\u675f\u66f4\u65b0\u663e\u793a\n * @gpio.reset: GPIO used to reset display reset\u7528\u4e8e\u590d\u4f4d\u663e\u793a\u7684gpio\n * @gpio.dc: Data\/Command signal, also known as RS dc\u6570\u636e\/\u547d\u4ee4\u4fe1\u53f7\u4e5f\u79f0\u4e3ars\n * @gpio.rd: Read latching signal \u8bfb\u53d6\u9501\u5b58\u4fe1\u53f7\n * @gpio.wr: Write latching signal \u5199\u9501\u5b58\u4fe1\u53f7\n * @gpio.latch: Bus latch signal, eg. 16->8 bit bus latch \u9501\u5b58\uff1a\u603b\u7ebf\u9501\u5b58\u4fe1\u53f7 \u598216->8\u4f4d\u603b\u7ebf\u9501\u5b58\n * @gpio.cs: LCD Chip Select with parallel interface bus lcd\u82af\u7247\u9009\u62e9\u5e26\u5e76\u884c\u63a5\u53e3\u603b\u7ebf\n * @gpio.db[16]: Parallel databus \u5e76\u884c\u6570\u636e\u603b\u7ebf\n * @gpio.led[16]: Led control signals led\u63a7\u5236\u4fe1\u53f7\n * @gpio.aux[16]: Auxiliary signals, not used by core \u8f85\u52a9\u4fe1\u53f7\uff0c\u4e0d\u88abcore\u4f7f\u7528\n * @init_sequence: Pointer to LCD initialization array \u6307\u5411lcd\u521d\u59cb\u5316\u6570\u7ec4\u7684\u6307\u9488\n * @gamma.lock: Mutex for Gamma curve locking gamma\u66f2\u7ebf\u9501\u7684\u4e92\u65a5\u9501\n * @gamma.curves: Pointer to Gamma curve array \u6307\u5411gamma\u66f2\u7ebf\u6570\u7ec4\u7684\u6307\u9488\n * @gamma.num_values: Number of values per Gamma curve meiggamma\u66f2\u7ebf\u7684\u503c\u7684\u6570\u91cf\n * @gamma.num_curves: Number of Gamma curves gamma\u66f2\u7ebf\u7684\u4e2a\u6570\n * @debug: Pointer to debug value \u6307\u5411\u8c03\u8bd5\u503c\u5f97\u6307\u9488\n * @current_debug: \n * @first_update_done: Used to only time the first display update \u7528\u4e8e\u8ba1\u7b97\u7b2c\u4e00\u6b21\u663e\u793a\u66f4\u65b0\u5f97\u65f6\u95f4\n * @update_time: Used to calculate 'fps' in debug output \u7528\u4e8e\u8ba1\u7b97\u8c03\u8bd5\u8f93\u51fa\u4e2d\u5f97\u201cfps\u201d\n * @bgr: BGR mode\/\\n  bgr\u6a21\u5f0f\n * @extra: Extra info needed by driver \u9a71\u52a8\u7a0b\u5e8f\u9700\u8981\u5f97\u989d\u5916\u4fe1\u606f\n *\/\nstruct fbtft_par {\n    struct spi_device *spi;\n    struct platform_device *pdev;\n    struct fb_info *info;\n    struct fbtft_platform_data *pdata;\n    u16 *ssbuf;\n    u32 pseudo_palette[16];\n    struct {\n        void *buf;\n        size_t len;\n    } txbuf;\n    u8 *buf;\n    u8 startbyte;\n    struct fbtft_ops fbtftops;\n    spinlock_t dirty_lock;\n    unsigned int dirty_lines_start;\n    unsigned int dirty_lines_end;\n    struct {\n        struct gpio_desc *reset;\n        struct gpio_desc *dc;\n        struct gpio_desc *rd;\n        struct gpio_desc *wr;\n        struct gpio_desc *latch;\n        struct gpio_desc *cs;\n        struct gpio_desc *db[16];\n        struct gpio_desc *led[16];\n        struct gpio_desc *aux[16];\n    } gpio;\n    const s16 *init_sequence;\n    struct {\n        struct mutex lock;\n        u32 *curves;\n        int num_values;\n        int num_curves;\n    } gamma;\n    unsigned long debug;\n    bool first_update_done;\n    ktime_t update_time;\n    bool bgr;\n    void *extra;\n    bool polarity;\n};\n\n\/**\n * struct fbtft_gpio - Structure that holds one pinname to gpio mapping  \u4fdd\u5b58\u4e00\u4e2a\u5230gpio\u6620\u5c04\u5f97pin\u540d\u5f97\u7ed3\u6784\n * @name: pinname (reset, dc, etc.)\n * @gpio: GPIO number\n *\n *\/\nstruct fbtft_gpio {\n    char name[FBTFT_GPIO_NAME_SIZE];\n    struct gpio_desc *gpio;\n};\n\n\/***********************************************************\u9a71\u52a8\u6240\u9700\u7ed3\u6784\u4f53\u7ed3\u675f********************************************************************\/\n\n\/****************************************************fbtft-bus.c\u6587\u4ef6\u7684\u51fd\u6570\u5b8f\u5f00\u59cb***********************************************************\/\n\n#define define_fbtft_write_reg(func, buffer_type, data_type, modifier)        \\\nvoid func(struct fbtft_par *par, int len, ...)                                \\\n{                                                                             \\\n    va_list args;                                                         \\\n    int i, ret;                                                           \\\n    int offset = 0;                                                       \\\n    buffer_type *buf = (buffer_type *)par->buf;                           \\\n                                          \\\n    if (unlikely(par->debug & DEBUG_WRITE_REGISTER)) {                    \\\n        va_start(args, len);                                          \\\n        for (i = 0; i < len; i++) {                                   \\\n            buf[i] = modifier((data_type)va_arg(args,             \\\n                                unsigned int));   \\\n        }                                                             \\\n        va_end(args);                                                 \\\n        fbtft_par_dbg_hex(DEBUG_WRITE_REGISTER, par,                  \\\n                  par->info->device, buffer_type, buf, len,   \\\n                  \"%s: \", __func__);                          \\\n    }                                                                     \\\n                                          \\\n    va_start(args, len);                                                  \\\n                                          \\\n    if (par->startbyte) {                                                 \\\n        *(u8 *)par->buf = par->startbyte;                             \\\n        buf = (buffer_type *)(par->buf + 1);                          \\\n        offset = 1;                                                   \\\n    }                                                                     \\\n                                          \\\n    *buf = modifier((data_type)va_arg(args, unsigned int));               \\\n    ret = fbtft_write_buf_dc(par, par->buf, sizeof(data_type) + offset,   \\\n                 0);                                          \\\n    if (ret < 0)                                  \\\n        goto out;                             \\\n    len--;                                                                \\\n                                          \\\n    if (par->startbyte)                                                   \\\n        *(u8 *)par->buf = par->startbyte | 0x2;                       \\\n                                          \\\n    if (len) {                                                            \\\n        i = len;                                                      \\\n        while (i--)                           \\\n            *buf++ = modifier((data_type)va_arg(args,             \\\n                                unsigned int));   \\\n        fbtft_write_buf_dc(par, par->buf,                 \\\n                   len * (sizeof(data_type) + offset), 1);    \\\n    }                                                                     \\\nout:                                          \\\n    va_end(args);                                                         \\\n}                                                                           \n\n#define NUMARGS(...)  (sizeof((int[]){__VA_ARGS__}) \/ sizeof(int))\n\n#define write_reg(par, ...)                                            \\\n    ((par)->fbtftops.write_register(par, NUMARGS(__VA_ARGS__), __VA_ARGS__))\n\n#define fbtft_init_dbg(dev, format, arg...)                  \\\ndo {                                                         \\\n    if (unlikely((dev)->platform_data &&                 \\\n        (((struct fbtft_platform_data *)(dev)->platform_data)->display.debug & DEBUG_DRIVER_INIT_FUNCTIONS))) \\\n        dev_info(dev, format, ##arg);                \\\n} while (0)\n\n#define fbtft_par_dbg(level, par, format, arg...)            \\\ndo {                                                         \\\n    if (unlikely((par)->debug & (level)))                    \\\n        dev_info((par)->info->device, format, ##arg);  \\\n} while (0)\n\n#define fbtft_par_dbg_hex(level, par, dev, type, buf, num, format, arg...) \\\ndo {                                                                       \\\n    if (unlikely((par)->debug & (level)))                                  \\\n        fbtft_dbg_hex(dev, sizeof(type), buf,\\\n                  (num) * sizeof(type), format, ##arg); \\\n} while (0)\n\n\/************************************************fbtft-bus.c\u6587\u4ef6\u7684\u51fd\u6570\u5b8f\u7ed3\u675f***************************************************************\/\n\n\/*****************************************************\u5e73\u53f0\u603b\u7ebf\u5224\u65ad\u51fd\u6570\u5b9e\u73b0*********************************************************************\/\n\n#define FBTFT_REGISTER_DRIVER(_name, _compatible, _display)                \\\n                                       \\\nstatic int fbtft_driver_probe_spi(struct spi_device *spi)                  \\\n{                                                                          \\\n    return fbtft_probe_common(_display, spi, NULL);                    \\\n}                                                                          \\\n                                       \\\nstatic int fbtft_driver_remove_spi(struct spi_device *spi)                 \\\n{                                                                          \\\n    struct fb_info *info = spi_get_drvdata(spi);                       \\\n                                       \\\n    return fbtft_remove_common(&spi->dev, info);                       \\\n}                                                                          \\\n                                       \\\nstatic int fbtft_driver_probe_pdev(struct platform_device *pdev)           \\\n{                                                                          \\\n    return fbtft_probe_common(_display, NULL, pdev);                   \\\n}                                                                          \\\n                                       \\\nstatic int fbtft_driver_remove_pdev(struct platform_device *pdev)          \\\n{                                                                          \\\n    struct fb_info *info = platform_get_drvdata(pdev);                 \\\n                                       \\\n    return fbtft_remove_common(&pdev->dev, info);                      \\\n}                                                                          \\\n                                       \\\nstatic const struct of_device_id dt_ids[] = {                              \\\n    { .compatible = _compatible },                                     \\\n    {},                                                                \\\n};                                                                         \\\n                                       \\\nMODULE_DEVICE_TABLE(of, dt_ids);                                           \\\n                                       \\\n                                       \\\nstatic struct spi_driver fbtft_driver_spi_driver = {                       \\\n    .driver = {                                                        \\\n        .name   = _name,                                           \\\n        .of_match_table = dt_ids,                                  \\\n    },                                                                 \\\n    .probe  = fbtft_driver_probe_spi,                                  \\\n    .remove = fbtft_driver_remove_spi,                                 \\\n};                                                                         \\\n                                       \\\nstatic struct platform_driver fbtft_driver_platform_driver = {             \\\n    .driver = {                                                        \\\n        .name   = _name,                                           \\\n        .owner  = THIS_MODULE,                                     \\\n        .of_match_table = dt_ids,                                  \\\n    },                                                                 \\\n    .probe  = fbtft_driver_probe_pdev,                                 \\\n    .remove = fbtft_driver_remove_pdev,                                \\\n};                                                                         \\\n                                       \\\nstatic int __init fbtft_driver_module_init(void)                           \\\n{                                                                          \\\n    int ret;                                                           \\\n                                       \\\n    ret = spi_register_driver(&fbtft_driver_spi_driver);               \\\n    if (ret < 0)                                                       \\\n        return ret;                                                \\\n    ret = platform_driver_register(&#038;fbtft_driver_platform_driver);     \\\n    if (ret < 0)                                                       \\\n        spi_unregister_driver(&#038;fbtft_driver_spi_driver);           \\\n    return ret;                                                        \\\n}                                                                          \\\n                                       \\\nstatic void __exit fbtft_driver_module_exit(void)                          \\\n{                                                                          \\\n    spi_unregister_driver(&#038;fbtft_driver_spi_driver);                   \\\n    platform_driver_unregister(&#038;fbtft_driver_platform_driver);         \\\n}                                                                          \\\n                                       \\\nmodule_init(fbtft_driver_module_init);                                     \\\nmodule_exit(fbtft_driver_module_exit);\n\n\/********************************************************\u5e73\u53f0\u603b\u7ebf\u5224\u65ad\u7ed3\u675f*****************************************************************\/\n\n\/**********************************************************\u5176\u4ed6\u6587\u4ef6\u5f97\u51fd\u6570***********************************************************************************\/\n\/* fbtft-core.c *\/\n int fbtft_write_buf_dc(struct fbtft_par *par, void *buf, size_t len, int dc);\n\/\/ __printf(5, 6)\n void fbtft_dbg_hex(const struct device *dev, int groupsize,\n           void *buf, size_t len, const char *fmt, ...);\nstruct fb_info *fbtft_framebuffer_alloc(struct fbtft_display *display,\n                    struct device *dev,\n                    struct fbtft_platform_data *pdata);\nvoid fbtft_framebuffer_release(struct fb_info *info);\nint fbtft_register_framebuffer(struct fb_info *fb_info);\nint fbtft_unregister_framebuffer(struct fb_info *fb_info);\nvoid fbtft_register_backlight(struct fbtft_par *par);\nvoid fbtft_unregister_backlight(struct fbtft_par *par);\nint fbtft_init_display(struct fbtft_par *par);\nint fbtft_probe_common(struct fbtft_display *display, struct spi_device *sdev,\n               struct platform_device *pdev);\nint fbtft_remove_common(struct device *dev, struct fb_info *info);\n\n\/* fbtft-io.c *\/\nint fbtft_write_spi(struct fbtft_par *par, void *buf, size_t len);\nint fbtft_write_spi_emulate_9(struct fbtft_par *par, void *buf, size_t len);\nint fbtft_read_spi(struct fbtft_par *par, void *buf, size_t len);\nint fbtft_write_gpio8_wr(struct fbtft_par *par, void *buf, size_t len);\nint fbtft_write_gpio16_wr(struct fbtft_par *par, void *buf, size_t len);\nint fbtft_write_gpio16_wr_latched(struct fbtft_par *par, void *buf, size_t len);\n\n\/* fbtft-bus.c *\/\nint fbtft_write_vmem8_bus8(struct fbtft_par *par, size_t offset, size_t len);\nint fbtft_write_vmem16_bus16(struct fbtft_par *par, size_t offset, size_t len);\nint fbtft_write_vmem16_bus8(struct fbtft_par *par, size_t offset, size_t len);\nint fbtft_write_vmem16_bus9(struct fbtft_par *par, size_t offset, size_t len);\nvoid fbtft_write_reg8_bus8(struct fbtft_par *par, int len, ...);\nvoid fbtft_write_reg8_bus9(struct fbtft_par *par, int len, ...);\nvoid fbtft_write_reg16_bus8(struct fbtft_par *par, int len, ...);\nvoid fbtft_write_reg16_bus16(struct fbtft_par *par, int len, ...);\n\n\/***********************************************************\u5176\u4ed6\u6587\u4ef6\u5f97\u51fd\u6570\u7ed3\u675f************************************************************\/\n\n\/**********************************\u7279\u6b8a\u5b8f*********************************************************************\/\n\nvoid fbtft_sysfs_init(struct fbtft_par *par);\nvoid fbtft_sysfs_exit(struct fbtft_par *par);\nvoid fbtft_expand_debug_value(unsigned long *debug);\nint fbtft_gamma_parse_str(struct fbtft_par *par, u32 *curves,\n                          const char *str, int size);\n\n#define FBTFT_ONBOARD_BACKLIGHT 2\n\n#define FBTFT_GPIO_NO_MATCH     0xFFFF\n#define FBTFT_GPIO_NAME_SIZE    32\n#define FBTFT_MAX_INIT_SEQUENCE      512\n#define FBTFT_GAMMA_MAX_VALUES_TOTAL 128\n\n#define FBTFT_OF_INIT_CMD   BIT(24)\n#define FBTFT_OF_INIT_DELAY BIT(25)\n\/* Debug macros *\/\n\n\/* shorthand debug levels *\/\n#define DEBUG_LEVEL_1   DEBUG_REQUEST_GPIOS\n#define DEBUG_LEVEL_2   (DEBUG_LEVEL_1 | DEBUG_DRIVER_INIT_FUNCTIONS        \\\n                       | DEBUG_TIME_FIRST_UPDATE)\n#define DEBUG_LEVEL_3   (DEBUG_LEVEL_2 | DEBUG_RESET | DEBUG_INIT_DISPLAY   \\\n                       | DEBUG_BLANK | DEBUG_REQUEST_GPIOS  \\\n                       | DEBUG_FREE_GPIOS                   \\\n                       | DEBUG_VERIFY_GPIOS                 \\\n                       | DEBUG_BACKLIGHT | DEBUG_SYSFS)\n#define DEBUG_LEVEL_4   (DEBUG_LEVEL_2 | DEBUG_FB_READ | DEBUG_FB_WRITE     \\\n                       | DEBUG_FB_FILLRECT                  \\\n                       | DEBUG_FB_COPYAREA                  \\\n                       | DEBUG_FB_IMAGEBLIT | DEBUG_FB_BLANK)\n#define DEBUG_LEVEL_5   (DEBUG_LEVEL_3 | DEBUG_UPDATE_DISPLAY)\n#define DEBUG_LEVEL_6   (DEBUG_LEVEL_4 | DEBUG_LEVEL_5)\n#define DEBUG_LEVEL_7   0xFFFFFFFF\n\n#define DEBUG_DRIVER_INIT_FUNCTIONS BIT(3)\n#define DEBUG_TIME_FIRST_UPDATE     BIT(4)\n#define DEBUG_TIME_EACH_UPDATE      BIT(5)\n#define DEBUG_DEFERRED_IO           BIT(6)\n#define DEBUG_FBTFT_INIT_FUNCTIONS  BIT(7)\n\n\/* fbops *\/\n#define DEBUG_FB_READ               BIT(8)\n#define DEBUG_FB_WRITE              BIT(9)\n#define DEBUG_FB_FILLRECT           BIT(10)\n#define DEBUG_FB_COPYAREA           BIT(11)\n#define DEBUG_FB_IMAGEBLIT          BIT(12)\n#define DEBUG_FB_SETCOLREG          BIT(13)\n#define DEBUG_FB_BLANK              BIT(14)\n\n#define DEBUG_SYSFS                 BIT(16)\n\n\/* fbtftops *\/\n#define DEBUG_BACKLIGHT             BIT(17)\n#define DEBUG_READ                  BIT(18)\n#define DEBUG_WRITE                 BIT(19)\n#define DEBUG_WRITE_VMEM            BIT(20)\n#define DEBUG_WRITE_REGISTER        BIT(21)\n#define DEBUG_SET_ADDR_WIN          BIT(22)\n#define DEBUG_RESET                 BIT(23)\n#define DEBUG_MKDIRTY               BIT(24)\n#define DEBUG_UPDATE_DISPLAY        BIT(25)\n#define DEBUG_INIT_DISPLAY          BIT(26)\n#define DEBUG_BLANK                 BIT(27)\n#define DEBUG_REQUEST_GPIOS         BIT(28)\n#define DEBUG_FREE_GPIOS            BIT(29)\n#define DEBUG_REQUEST_GPIOS_MATCH   BIT(30)\n#define DEBUG_VERIFY_GPIOS          BIT(31)\n\n\/************************************\u5b8f\u503c\u7ed3\u675f*******************************************************************\/\n\n\/******************************************fbtft-core.c\u6587\u4ef6\u4e3b\u8981\u51fd\u6570********************************************************************\/\nstatic unsigned long debug;\nmodule_param(debug, ulong, 0000);\nMODULE_PARM_DESC(debug, \"override device debug level\");\n\n\/\/\u5411dc\u5f15\u811a\u5199\u5165\u6570\u636e\nint fbtft_write_buf_dc(struct fbtft_par *par, void *buf, size_t len, int dc)\n{\n    int ret;\n\n    gpiod_set_value(par->gpio.dc, dc);\n\n    ret = par->fbtftops.write(par, buf, len);\n    if (ret < 0)\n        dev_err(par->info->device,\n            \"write() failed and returned %d\\n\", ret);\n    return ret;\n}\n\n\/\/\u8c03\u8bd5\u51fd\u6570 fbtft_dbg_hex\uff0c\u7528\u4e8e\u5c06\u5185\u5b58\u7f13\u51b2\u533a\u4e2d\u7684\u6570\u636e\u4ee5\u5341\u516d\u8fdb\u5236\u5f62\u5f0f\u8f93\u51fa\u5230\u65e5\u5fd7\u4e2d\nvoid fbtft_dbg_hex(const struct device *dev, int groupsize,\n           void *buf, size_t len, const char *fmt, ...)\n{\n    va_list args;\n    static char textbuf[512];\n    char *text = textbuf;\n    size_t text_len;\n\n    va_start(args, fmt);\n    text_len = vscnprintf(text, sizeof(textbuf), fmt, args);\n    va_end(args);\n\n    hex_dump_to_buffer(buf, len, 32, groupsize, text + text_len,\n               512 - text_len, false);\n\n    if (len > 32)\n        dev_info(dev, \"%s ...\\n\", text);\n    else\n        dev_info(dev, \"%s\\n\", text);\n}\n\n\/\/\u83b7\u53d6gpio\u5f97\u72b6\u6001\u5e76\u8bbe\u7f6e\u4e3a\u6709\u6548\nstatic int fbtft_request_one_gpio(struct fbtft_par *par,\n                  const char *name, int index,\n                  struct gpio_desc **gpiop)\n{\n    struct device *dev = par->info->device;\n    struct device_node *node = dev->of_node;\n    int gpio, flags, ret = 0;\n    enum of_gpio_flags of_flags;\n    if (of_find_property(node, name, NULL)) {\n        gpio = of_get_named_gpio_flags(node, name, index, &of_flags);\n        if (gpio == -ENOENT)\n            return 0;\n        if (gpio == -EPROBE_DEFER)\n            return gpio;\n        if (gpio < 0) {\n            dev_err(dev,\n                \"failed to get '%s' from DT\\n\", name);\n            return gpio;\n        }\n         \/\/active low translates to initially low\uff0c\u5982\u679cgpio\u4e3a\u4f4e\u7535\u5e73\u6709\u6548\uff0c\u5c31\u8bbe\u7f6e\u4e3a\u4f4e\uff0c\u5426\u5219\u8bbe\u7f6e\u4e3a\u9ad8\n        flags = (of_flags &#038; OF_GPIO_ACTIVE_LOW) ? GPIOF_OUT_INIT_LOW :\n                            GPIOF_OUT_INIT_HIGH;\n        ret = devm_gpio_request_one(dev, gpio, flags,\n                        dev->driver->name);\n        if (ret) {\n            dev_err(dev,\n                \"gpio_request_one('%s'=%d) failed with %d\\n\",\n                name, gpio, ret);\n            return ret;\n        }\n\n        *gpiop = gpio_to_desc(gpio);\n        fbtft_par_dbg(DEBUG_REQUEST_GPIOS, par, \"%s: '%s' = GPIO%d\\n\",__func__, name, gpio);\n    }\n\n    return ret;\n}\n\nstatic int fbtft_request_gpios(struct fbtft_par *par)\n{\n    int i;\n    int ret;\n\n    ret = fbtft_request_one_gpio(par, \"reset\", 0, &par->gpio.reset);\n    if (ret)\n        return ret;\n    ret = fbtft_request_one_gpio(par, \"dc\", 0, &par->gpio.dc);\n    if (ret)\n        return ret;\n    ret = fbtft_request_one_gpio(par, \"rd\", 0, &par->gpio.rd);  \/\/\u8bfb\u53d6\u9501\u5b58\u4fe1\u53f7\n    if (ret)\n        return ret;\n    ret = fbtft_request_one_gpio(par, \"wr\", 0, &par->gpio.wr);\n    if (ret)\n        return ret;\n    ret = fbtft_request_one_gpio(par, \"cs\", 0, &par->gpio.cs);\n    if (ret)\n        return ret;\n    ret = fbtft_request_one_gpio(par, \"latch\", 0, &par->gpio.latch);  \/\/\u9501\u5b58\u4fe1\u53f7\n    if (ret)\n        return ret;\n    for (i = 0; i < 16; i++) {\n        ret = fbtft_request_one_gpio(par, \"db\", i,   \/\/\u5e76\u884c\u6570\u636e\u603b\u7ebf\n                         &#038;par->gpio.db[i]);\n        if (ret)\n            return ret;\n        ret = fbtft_request_one_gpio(par, \"led\", i,\n                         &par->gpio.led[i]);   \/\/led\u63a7\u5236\u4fe1\u53f7\n        if (ret)\n            return ret;\n        ret = fbtft_request_one_gpio(par, \"aux\", i, \/\/\u8f85\u52a9\u4fe1\u53f7\n                         &par->gpio.aux[i]);\n        if (ret)\n            return ret;\n    }\n\n    return 0;\n}\n\n\/\/\u66f4\u65b0\u5c4f\u5e55\u80cc\u5149\u72b6\u6001\nstatic int fbtft_backlight_update_status(struct backlight_device *bd)\n{\n    struct fbtft_par *par = bl_get_data(bd);\n    bool polarity = par->polarity;\n\n    fbtft_par_dbg(DEBUG_BACKLIGHT, par,\n              \"%s: polarity=%d, power=%d, fb_blank=%d\\n\",\n              __func__, polarity, bd->props.power, bd->props.fb_blank);\n\n    if ((bd->props.power == FB_BLANK_UNBLANK) &&\n        (bd->props.fb_blank == FB_BLANK_UNBLANK))\n        gpiod_set_value(par->gpio.led[0], polarity);\n    else\n        gpiod_set_value(par->gpio.led[0], !polarity);\n\n    return 0;\n}\n\n\/\/\u83b7\u53d6\u5c4f\u5e55\u80cc\u5149\u4eae\u5ea6\nstatic int fbtft_backlight_get_brightness(struct backlight_device *bd)\n{\n    return bd->props.brightness;\n}\n\/\/\u6ce8\u9500\u5c4f\u5e55\u80cc\u5149\u8bbe\u5907\nvoid fbtft_unregister_backlight(struct fbtft_par *par)\n{\n    if (par->info->bl_dev) {\n        par->info->bl_dev->props.power = FB_BLANK_POWERDOWN;\n        backlight_update_status(par->info->bl_dev);\n        backlight_device_unregister(par->info->bl_dev);\n        par->info->bl_dev = NULL;\n    }\n}\n\n\/\/\u80cc\u5149\u8bbe\u5907\u7684\u64cd\u4f5c\u65b9\u6cd5\u7ed3\u6784\u4f53\nstatic const struct backlight_ops fbtft_bl_ops = {\n    .get_brightness = fbtft_backlight_get_brightness,\n    .update_status  = fbtft_backlight_update_status,\n};\n\/\/\u6ce8\u518c\u5c4f\u5e55\u80cc\u5149\u8bbe\u5907\nvoid fbtft_register_backlight(struct fbtft_par *par)\n{\n    struct backlight_device *bd;\n    struct backlight_properties bl_props = { 0, };\n\n    if (!par->gpio.led[0]) {\n        fbtft_par_dbg(DEBUG_BACKLIGHT, par,\n                  \"%s(): led pin not set, exiting.\\n\", __func__);\n        return;\n    }\n\n    bl_props.type = BACKLIGHT_RAW;\n    \/* Assume backlight is off, get polarity from current state of pin *\/\n    bl_props.power = FB_BLANK_POWERDOWN;\n    if (!gpiod_get_value(par->gpio.led[0]))\n        par->polarity = true;\n\n    bd = backlight_device_register(dev_driver_string(par->info->device),\n                       par->info->device, par,\n                       &fbtft_bl_ops, &bl_props);\n    if (IS_ERR(bd)) {\n        dev_err(par->info->device,\n            \"cannot register backlight device (%ld)\\n\",\n            PTR_ERR(bd));\n        return;\n    }\n    par->info->bl_dev = bd;\n\n    if (!par->fbtftops.unregister_backlight)\n        par->fbtftops.unregister_backlight = fbtft_unregister_backlight;\n}\n\n\/\/\u8bbe\u7f6etft\u663e\u793a\u5c4f\u7684\u5730\u5740\u7a97\u53e3\uff0c\u76ee\u7684\u662f\u5728\u63a5\u4e0b\u6765\u5199\u5165\u6570\u636e\u64cd\u4f5c\u53ea\u4f1a\u5f71\u54cd\u8be5\u7a97\u53e3\u5185\u7684\u50cf\u7d20\u533a\u57df\nstatic void fbtft_set_addr_win(struct fbtft_par *par, int xs, int ys, int xe,\n                   int ye)\n{\n    write_reg(par, MIPI_DCS_SET_COLUMN_ADDRESS,\n          (xs >> 8) & 0xFF, xs & 0xFF, (xe >> 8) & 0xFF, xe & 0xFF);\n\n    write_reg(par, MIPI_DCS_SET_PAGE_ADDRESS,\n          (ys >> 8) & 0xFF, ys & 0xFF, (ye >> 8) & 0xFF, ye & 0xFF);\n\n    write_reg(par, MIPI_DCS_WRITE_MEMORY_START);\n}\n\n\/\/\u590d\u4f4d\u5c4f\u5e55\nstatic void fbtft_reset(struct fbtft_par *par)\n{\n    if (!par->gpio.reset)\n        return;\n\n    fbtft_par_dbg(DEBUG_RESET, par, \"%s()\\n\", __func__);\n\n    gpiod_set_value_cansleep(par->gpio.reset, 0);\n    usleep_range(20, 40);\n    gpiod_set_value_cansleep(par->gpio.reset, 1);\n    msleep(120);\n\n    gpiod_set_value_cansleep(par->gpio.cs, 0);  \/* Activate chip *\/\/\/\u51fd\u6570\u7528\u4e8e\u8bbe\u7f6e GPIO \u5f15\u811a\u7684\u503c\uff0c\u5e76\u4e14\u5728\u9700\u8981\u65f6\u5141\u8bb8\u5728\u53ef\u4ee5\u7761\u7720\u7684\u4e0a\u4e0b\u6587\u4e2d\u8fd0\u884c\u3002\n}\n\n\/\/\u66f4\u65b0\u663e\u793a\u5c4f\u7684\u5185\u5bb9\uff0c\u6839\u636e\u8d77\u59cb\u884c\u548c\u7ed3\u675f\u884c\u7684\uff0c\u5c06\u5bf9\u5e94\u7684\u663e\u5b58\u5185\u5bb9\u5199\u5165\u5230\u663e\u793a\u5c4f\nstatic void fbtft_update_display(struct fbtft_par *par, unsigned int start_line,\n                 unsigned int end_line)\n{\n    size_t offset, len;\n    ktime_t ts_start, ts_end;\n    long fps, throughput;\n    bool timeit = false;\n    int ret = 0;\n\n    if (unlikely(par->debug & (DEBUG_TIME_FIRST_UPDATE |\n            DEBUG_TIME_EACH_UPDATE))) {\n        if ((par->debug & DEBUG_TIME_EACH_UPDATE) ||\n            ((par->debug & DEBUG_TIME_FIRST_UPDATE) &&\n            !par->first_update_done)) {\n            ts_start = ktime_get();\n            timeit = true;\n        }\n    }\n\n    \/* Sanity checks *\/\n    if (start_line > end_line) {\n        dev_warn(par->info->device,\n             \"%s: start_line=%u is larger than end_line=%u. Shouldn't happen, will do full display update\\n\",\n             __func__, start_line, end_line);\n        start_line = 0;\n        end_line = par->info->var.yres - 1;\n    }\n    if (start_line > par->info->var.yres - 1 ||\n        end_line > par->info->var.yres - 1) {\n        dev_warn(par->info->device,\n             \"%s: start_line=%u or end_line=%u is larger than max=%d. Shouldn't happen, will do full display update\\n\",\n             __func__, start_line,\n             end_line, par->info->var.yres - 1);\n        start_line = 0;\n        end_line = par->info->var.yres - 1;\n    }\n\n    fbtft_par_dbg(DEBUG_UPDATE_DISPLAY, par, \"%s(start_line=%u, end_line=%u)\\n\",\n              __func__, start_line, end_line);\n\n    if (par->fbtftops.set_addr_win)\n        par->fbtftops.set_addr_win(par, 0, start_line,\n                par->info->var.xres - 1, end_line);\n\n    offset = start_line * par->info->fix.line_length;\n    len = (end_line - start_line + 1) * par->info->fix.line_length;\n    ret = par->fbtftops.write_vmem(par, offset, len);\n    if (ret < 0)\n        dev_err(par->info->device,\n            \"%s: write_vmem failed to update display buffer\\n\",\n            __func__);\n\n    if (unlikely(timeit)) {\n        ts_end = ktime_get();\n        if (!ktime_to_ns(par->update_time))\n            par->update_time = ts_start;\n\n        fps = ktime_us_delta(ts_start, par->update_time);\n        par->update_time = ts_start;\n        fps = fps ? 1000000 \/ fps : 0;\n\n        throughput = ktime_us_delta(ts_end, ts_start);\n        throughput = throughput ? (len * 1000) \/ throughput : 0;\n        throughput = throughput * 1000 \/ 1024;\n\n        dev_info(par->info->device,\n             \"Display update: %ld kB\/s, fps=%ld\\n\",\n             throughput, fps);\n        par->first_update_done = true;\n    }\n}\n\n\/\/\u6807\u8bb0\u663e\u793a\u5c4f\u7684\u4e00\u90e8\u5206\u533a\u57df\u4e3a\u810f\u533a\u57df\uff0c\u8868\u793a\u8be5\u533a\u57df\u9700\u8981\u66f4\u65b0\uff0c\u5e76\u5b89\u6392\u5ef6\u8fdf\u5de5\u4f5c\u6765\u6267\u884c\u5b9e\u9645\u7684\u663e\u793a\u66f4\u65b0\nstatic void fbtft_mkdirty(struct fb_info *info, int y, int height)\n{\n    struct fbtft_par *par = info->par;\n    struct fb_deferred_io *fbdefio = info->fbdefio;\n\n    \/* special case, needed ? *\/\n    if (y == -1) {\n        y = 0;\n        height = info->var.yres;\n    }\n\n    \/* Mark display lines\/area as dirty *\/\n    spin_lock(&par->dirty_lock);\n    if (y < par->dirty_lines_start)\n        par->dirty_lines_start = y;\n    if (y + height - 1 > par->dirty_lines_end)\n        par->dirty_lines_end = y + height - 1;\n    spin_unlock(&par->dirty_lock);\n\n    \/* Schedule deferred_io to update display (no-op if already on queue)*\/\n    schedule_delayed_work(&info->deferred_work, fbdefio->delay);\n}\n\n\/\/\u5904\u7406\u5e27\u7f13\u51b2\u8bbe\u5907\u7684\u5ef6\u8fdf\u8f93\u5165\/\u8f93\u51fa\uff0c\u6807\u8bb0\u9700\u8981\u66f4\u65b0\u7684\u663e\u793a\u533a\u57df\uff0c\u5e76\u8c03\u7528\u66f4\u65b0\u663e\u793a\u51fd\u6570\nstatic void fbtft_deferred_io(struct fb_info *info, struct list_head *pagelist)\n{\n    struct fbtft_par *par = info->par;\n    unsigned int dirty_lines_start, dirty_lines_end;\n    struct page *page;\n    unsigned long index;\n    unsigned int y_low = 0, y_high = 0;\n    int count = 0;\n\n    spin_lock(&par->dirty_lock);\n    dirty_lines_start = par->dirty_lines_start;\n    dirty_lines_end = par->dirty_lines_end;\n    \/* set display line markers as clean *\/\n    par->dirty_lines_start = par->info->var.yres - 1;\n    par->dirty_lines_end = 0;\n    spin_unlock(&par->dirty_lock);\n\n    \/* Mark display lines as dirty *\/\n    list_for_each_entry(page, pagelist, lru) {\n        count++;\n        index = page->index << PAGE_SHIFT;\n        y_low = index \/ info->fix.line_length;\n        y_high = (index + PAGE_SIZE - 1) \/ info->fix.line_length;\n        dev_dbg(info->device,\n            \"page->index=%lu y_low=%d y_high=%d\\n\",\n            page->index, y_low, y_high);\n        if (y_high > info->var.yres - 1)\n            y_high = info->var.yres - 1;\n        if (y_low < dirty_lines_start)\n            dirty_lines_start = y_low;\n        if (y_high > dirty_lines_end)\n            dirty_lines_end = y_high;\n    }\n\n    par->fbtftops.update_display(info->par,\n                    dirty_lines_start, dirty_lines_end);\n}\n\n\/\/\u662f\u7528\u6765\u5904\u7406\u5e27\u7f13\u51b2\u8bbe\u5907\u4e2d\u7684\u586b\u5145\u77e9\u5f62\u64cd\u4f5c\u7684\u51fd\u6570\uff0c\u4ed6\u4f1a\u5728\u663e\u793a\u5c4f\u6307\u5b9a\u533a\u57df\u5185\u586b\u5145\u77e9\u5f62\uff0c\u6807\u8bb0\u8be5\u533a\u57df\u4e3a\u810f\u533a\u57df\n\/\/\u4fbf\u4e8e\u540e\u7eed\u663e\u793a\u66f4\u65b0\u64cd\u4f5c\nstatic void fbtft_fb_fillrect(struct fb_info *info,\n                  const struct fb_fillrect *rect)\n{\n    struct fbtft_par *par = info->par;\n\n    dev_dbg(info->dev,\n        \"%s: dx=%d, dy=%d, width=%d, height=%d\\n\",\n        __func__, rect->dx, rect->dy, rect->width, rect->height);\n    sys_fillrect(info, rect);\n\n    par->fbtftops.mkdirty(info, rect->dy, rect->height);\n}\n\n\/\/\u7528\u6765\u5904\u7406\u5e27\u7f13\u51b2\u8bbe\u5907\u4e2d\u7684\u533a\u57df\u590d\u5236\u64cd\u4f5c\uff0c\u5728\u6307\u5b9a\u533a\u57df\u6267\u884c\u590d\u5236\u64cd\u4f5c\uff0c\u6807\u8bb0\u8be5\u533a\u57df\u662f\u810f\u533a\u57df\uff0c\u65b9\u4fbf\u540e\u7eed\u64cd\u4f5c\nstatic void fbtft_fb_copyarea(struct fb_info *info,\n                  const struct fb_copyarea *area)\n{\n    struct fbtft_par *par = info->par;\n\n    dev_dbg(info->dev,\n        \"%s: dx=%d, dy=%d, width=%d, height=%d\\n\",\n        __func__,  area->dx, area->dy, area->width, area->height);\n    sys_copyarea(info, area);\n\n    par->fbtftops.mkdirty(info, area->dy, area->height);\n}\n\n\/\/\u7528\u6765\u5904\u7406\u56fe\u50cf\u7684\u4f4d\u5757\u64cd\u4f5c\uff0c\u5728\u6307\u5b9a\u533a\u57df\u7ed8\u5236\u56fe\u50cf\uff0c\u6807\u8bb0\u533a\u57df\u4e3a\u810f\u533a\u57df\uff0c\u540e\u7eed\u6267\u884c\u66f4\u65b0\nstatic void fbtft_fb_imageblit(struct fb_info *info,\n                   const struct fb_image *image)\n{\n    struct fbtft_par *par = info->par;\n\n    dev_dbg(info->dev,\n        \"%s: dx=%d, dy=%d, width=%d, height=%d\\n\",\n        __func__,  image->dx, image->dy, image->width, image->height);\n    sys_imageblit(info, image);\n\n    par->fbtftops.mkdirty(info, image->dy, image->height);\n}\n\n\/\/\u8bbe\u5907\u4e2d\u7528\u4e8e\u5904\u7406\u5199\u64cd\u4f5c\u7684\u51fd\u6570\uff0c\u5199\u5165\u7528\u6237\u7a7a\u95f4\u7684\u6570\u636e\u5230\u5e27\u7f13\u51b2\u8bbe\u5907\uff0c\u6807\u8bb0\u6574\u4e2a\u533a\u57df\u9700\u8981\u66f4\u65b0\nstatic ssize_t fbtft_fb_write(struct fb_info *info, const char __user *buf,\n                  size_t count, loff_t *ppos)\n{\n    struct fbtft_par *par = info->par;\n    ssize_t res;\n\n    dev_dbg(info->dev,\n        \"%s: count=%zd, ppos=%llu\\n\", __func__,  count, *ppos);\n    res = fb_sys_write(info, buf, count, ppos);\n\n    \/* TODO: only mark changed area update all for now *\/\n    par->fbtftops.mkdirty(info, -1, 0);\n\n    return res;\n}\n\n\/* from pxafb.c *\/\n\/\/\u5c06\u901a\u9053\u503c\u8f6c\u6362\u6210\u4e00\u4e2a\u5e27\u7f13\u51b2\u7684\u4f4d\u5b57\u6bb5\u503c\nstatic unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)\n{\n    chan &= 0xffff;\n    chan >>= 16 - bf->length;\n    return chan << bf->offset;\n}\n\n\/\/\u8bbe\u7f6e\u989c\u8272\u5bc4\u5b58\u5668\u7684\u51fd\u6570\nstatic int fbtft_fb_setcolreg(unsigned int regno, unsigned int red,\n                  unsigned int green, unsigned int blue,\n                  unsigned int transp, struct fb_info *info)\n{\n    unsigned int val;\n    int ret = 1;\n\n    dev_dbg(info->dev,\n        \"%s(regno=%u, red=0x%X, green=0x%X, blue=0x%X, trans=0x%X)\\n\",\n        __func__, regno, red, green, blue, transp);\n\n    switch (info->fix.visual) {\n    case FB_VISUAL_TRUECOLOR:\n        if (regno < 16) {\n            u32 *pal = info->pseudo_palette;\n\n            val  = chan_to_field(red,   &info->var.red);\n            val |= chan_to_field(green, &info->var.green);\n            val |= chan_to_field(blue,  &info->var.blue);\n\n            \/\/ val |= chan_to_field(red,   &info->var.red);\n            \/\/ val |= chan_to_field(green, &info->var.green);\n            \/\/ val = chan_to_field(blue,  &info->var.blue);\n\n            pal[regno] = val;\n            ret = 0;\n        }\n        break;\n    }\n    return ret;\n}\n\n\/\/\u63a7\u5236\u663e\u793a\u5c4f\u7684\u7a7a\u767d\u72b6\u6001\uff08\u5b9e\u73b0\u663e\u793a\u5668\u7684\u5f00\u5173\uff0c\u6302\u8d77\u548c\u6062\u590d\u64cd\u4f5c\uff09\nstatic int fbtft_fb_blank(int blank, struct fb_info *info)\n{\n    struct fbtft_par *par = info->par;\n    int ret = -EINVAL;\n\n    dev_dbg(info->dev, \"%s(blank=%d)\\n\",\n        __func__, blank);\n\n    if (!par->fbtftops.blank)\n        return ret;\n\n    switch (blank) {\n    case FB_BLANK_POWERDOWN:\n    case FB_BLANK_VSYNC_SUSPEND:\n    case FB_BLANK_HSYNC_SUSPEND:\n    case FB_BLANK_NORMAL:\n        ret = par->fbtftops.blank(par, true);\n        break;\n    case FB_BLANK_UNBLANK:\n        ret = par->fbtftops.blank(par, false);\n        break;\n    }\n    return ret;\n}\n\n\/\/\u5c06struct fbtft_ops\uff08src\uff09\u7ed3\u6784\u4f53\u6210\u5458\u590d\u5236\u5230\u53e6\u4e00\u4e2astruct fbtft_ops\uff08dts\uff09\u7ed3\u6784\u4f53\u4e2d\nstatic void fbtft_merge_fbtftops(struct fbtft_ops *dst, struct fbtft_ops *src)\n{\n    if (src->write)\n        dst->write = src->write;\n    if (src->read)\n        dst->read = src->read;\n    if (src->write_vmem)\n        dst->write_vmem = src->write_vmem;\n    if (src->write_register)\n        dst->write_register = src->write_register;\n    if (src->set_addr_win)\n        dst->set_addr_win = src->set_addr_win;\n    if (src->reset)\n        dst->reset = src->reset;\n    if (src->mkdirty)\n        dst->mkdirty = src->mkdirty;\n    if (src->update_display)\n        dst->update_display = src->update_display;\n    if (src->init_display)\n        dst->init_display = src->init_display;\n    if (src->blank)\n        dst->blank = src->blank;\n    if (src->request_gpios_match)\n        dst->request_gpios_match = src->request_gpios_match;\n    if (src->request_gpios)\n        dst->request_gpios = src->request_gpios;\n    if (src->verify_gpios)\n        dst->verify_gpios = src->verify_gpios;\n    if (src->register_backlight)\n        dst->register_backlight = src->register_backlight;\n    if (src->unregister_backlight)\n        dst->unregister_backlight = src->unregister_backlight;\n    if (src->set_var)\n        dst->set_var = src->set_var;\n    if (src->set_gamma)\n        dst->set_gamma = src->set_gamma;\n}\n\n\/**\n * fbtft_framebuffer_alloc - creates a new frame buffer info structure\n * \/\/\u521b\u5efa\u4e00\u4e2a\u65b0\u7684\u5e27\u7f13\u51b2\u533a\u4fe1\u606f\u7ed3\u6784\n *\n * @display: pointer to structure describing the display\n * \u6307\u5411\u63cf\u8ff0\u663e\u793a\u7684\u7ed3\u6784\u7684\u6307\u9488\n * @dev: pointer to the device for this fb, this can be NULL\n * \u6307\u5411\u8fd9\u4e2afb\u7684\u8bbe\u5907\u6307\u9488\uff0c\u53ef\u4ee5\u662fnull\n * @pdata: platform data for the display in use\n * \u6b63\u5728\u4f7f\u7528\u7684\u663e\u793a\u5668\u7684\u5e73\u53f0\u6570\u636e\n *\n * Creates a new frame buffer info structure.\n *\n * Also creates and populates the following structures:\n * \u53ef\u4ee5\u586b\u5145\u4e00\u4e0b\u7ed3\u6784\n *   info->fbops\n *   info->fbdefio\n *   info->pseudo_palette\n *   par->fbtftops\n *   par->txbuf\n *\n * Returns the new structure, or NULL if an error occurred.\n *\n *\/\nstruct fb_info *fbtft_framebuffer_alloc(struct fbtft_display *display,\n                    struct device *dev,\n                    struct fbtft_platform_data *pdata)\n{\n    struct fb_info *info;\n    struct fbtft_par *par;\n    struct fb_ops *fbops = NULL;\n    struct fb_deferred_io *fbdefio = NULL;\n    u8 *vmem = NULL;\n    void *txbuf = NULL;\n    void *buf = NULL;\n    unsigned int width;\n    unsigned int height;\n    int txbuflen = display->txbuflen;\n    unsigned int bpp = display->bpp;\n    unsigned int fps = display->fps;\n    int vmem_size;\n    const s16 *init_sequence = display->init_sequence;\n    char *gamma = display->gamma;\n    u32 *gamma_curves = NULL;\n    \/* sanity check *\/\n    \/\/\u5165\u53c2\u68c0\u67e5\n    if (display->gamma_num * display->gamma_len >\n            FBTFT_GAMMA_MAX_VALUES_TOTAL) {\n        dev_err(dev, \"FBTFT_GAMMA_MAX_VALUES_TOTAL=%d is exceeded\\n\",\n            FBTFT_GAMMA_MAX_VALUES_TOTAL);\n        return NULL;\n    }\n\n    \/* defaults *\/\n    \/\/\u82e5\u6ca1\u8bbe\u7f6e\u53c2\u6570\uff0c\u5219\u8bbe\u7f6e\u4e0b\u5217\u53c2\u6570\u4e3a\u9ed8\u8ba4\u53c2\u6570\n    if (!fps)   \/\/\u5e27\u7387\n        fps = 20;\n    if (!bpp)  \/\/\u6bd4\u7279\u6bcf\u50cf\u7d20\n        bpp = 16;\n        \/\/bpp = 24;\n\n    if (!pdata) {\n        dev_err(dev, \"platform data is missing\\n\");\n        return NULL;\n    }\n\n    \/* override driver values? *\/\n    \/\/\u8986\u76d6\u9a71\u52a8\u7a0b\u5e8f\u503c\uff1f\n    if (pdata->fps)\n        fps = pdata->fps;\n    if (pdata->txbuflen)\n        txbuflen = pdata->txbuflen;\n    if (pdata->display.init_sequence)\n        init_sequence = pdata->display.init_sequence;\n    if (pdata->gamma)\n        gamma = pdata->gamma;\n    if (pdata->display.debug)\n        display->debug = pdata->display.debug;\n    if (pdata->display.backlight)\n        display->backlight = pdata->display.backlight;\n    if (pdata->display.width)\n        display->width = pdata->display.width;\n    if (pdata->display.height)\n        display->height = pdata->display.height;\n    if (pdata->display.buswidth)\n        display->buswidth = pdata->display.buswidth;\n    if (pdata->display.regwidth)\n        display->regwidth = pdata->display.regwidth;\n\n    display->debug |= debug;\n    fbtft_expand_debug_value(&display->debug);\n\n   \/\/\u6839\u636e\u53ef\u80fd\u5b58\u5728\u7684\u89d2\u5ea6\u8c03\u6574\u957f\u5bbd\n    switch (pdata->rotate) {\n    case 90:\n    case 270:\n        width =  display->height;\n        height = display->width;\n        break;\n    default:\n        width =  display->width;\n        height = display->height;\n    }\n\/\/\u5206\u914d\u5185\u5b58\n    vmem_size = display->width * display->height * bpp \/ 8;  \/\/\u5e27\u7f13\u51b2\u533a\u7684\u5927\u5c0f\n    vmem = vzalloc(vmem_size);  \/\/\u5206\u914d\u5e27\u7f13\u51b2\u533a\n    if (!vmem)\n        goto alloc_fail;\n\/\/\u5206\u914dfb_ops\u7ed3\u6784\u4f53\u7684\u5185\u5b58\n    fbops = devm_kzalloc(dev, sizeof(struct fb_ops), GFP_KERNEL);\n    if (!fbops)\n        goto alloc_fail;\n\/\/\u5206\u914dfb_deferred_io\u7ed3\u6784\u4f53\u7684\u5185\u5b58\n    fbdefio = devm_kzalloc(dev, sizeof(struct fb_deferred_io), GFP_KERNEL);\n    if (!fbdefio)\n        goto alloc_fail;\n\/\/\u5206\u914d\u4e34\u65f6\u7f13\u51b2\u533a\n    buf = devm_kzalloc(dev, 128, GFP_KERNEL);\n    if (!buf)\n        goto alloc_fail;\n\n\/\/\u5206\u914dgamma\u66f2\u7ebf\u7684\u5185\u5b58\n    if (display->gamma_num && display->gamma_len) {\n        gamma_curves = devm_kcalloc(dev,\n                        display->gamma_num *\n                        display->gamma_len,\n                        sizeof(gamma_curves[0]),\n                        GFP_KERNEL);\n        if (!gamma_curves)\n            goto alloc_fail;\n    }\n\/\/\u5206\u914d\u5e27\u7f13\u51b2\u8bbe\u5907\n    info = framebuffer_alloc(sizeof(struct fbtft_par), dev);\n    if (!info)\n        goto alloc_fail;\n\n    info->screen_buffer = vmem;  \/\/\u6307\u5411\u5206\u914d\u7684\u663e\u793a\u7f13\u51b2\u533a\n    info->fbops = fbops;  \/\/\u6307\u5411\u5206\u914d\u7684fb_ops\u7ed3\u6784\u4f53\uff0c\n    \/\/\u521d\u59cb\u5316\u5176\u4e2d\u7684\u6210\u5458\u51fd\u6570\u6307\u9488\uff0c\u8fd9\u4e9b\u51fd\u6570\u5305\u62ec\u8bfb\u5199\u3001\u586b\u5145\u77e9\u5f62\u3001\n    \/\/\u590d\u5236\u533a\u57df\u3001\u56fe\u50cf\u4f20\u8f93\u3001\u989c\u8272\u5bc4\u5b58\u5668\u8bbe\u7f6e\u548c\u5c4f\u5e55\u7a7a\u767d\u5904\u7406\u7b49\u3002\n    info->fbdefio = fbdefio;  \/\/\u6307\u5411fb_deferred_io\u7ed3\u6784\u4f53\uff0c\u7528\u4e8e\u7ed3\u6784\u4f53\u7684\u5ef6\u8fdf\u548c\u5ef6\u8fdf\u5904\u7406\u51fd\u6570\n    \/\/\u521d\u59cb\u5316\u5e27\u5ef6\u8fdfI\/O\n\n    fbops->owner        =      dev->driver->owner;\n    fbops->fb_read      =      fb_sys_read;\n    fbops->fb_write     =      fbtft_fb_write;\n    fbops->fb_fillrect  =      fbtft_fb_fillrect;\n    fbops->fb_copyarea  =      fbtft_fb_copyarea;\n    fbops->fb_imageblit =      fbtft_fb_imageblit;\n    fbops->fb_setcolreg =      fbtft_fb_setcolreg;\n    fbops->fb_blank     =      fbtft_fb_blank;\n\n    fbdefio->delay =           HZ \/ fps;\n    fbdefio->deferred_io =     fbtft_deferred_io;\n    fb_deferred_io_init(info);\n\/\/\u521d\u59cb\u5316\u7ed3\u6784\u4f53\u7684\u56fa\u5b9a\u53c2\u6570\n    snprintf(info->fix.id, sizeof(info->fix.id), \"%s\", dev->driver->name);\n    info->fix.type =           FB_TYPE_PACKED_PIXELS;\n    info->fix.visual =         FB_VISUAL_TRUECOLOR;\n    info->fix.xpanstep =       0;\n    info->fix.ypanstep =       0;\n    info->fix.ywrapstep =      0;\n    info->fix.line_length =    width * bpp \/ 8;\n    info->fix.accel =          FB_ACCEL_NONE;\n    info->fix.smem_len =       vmem_size;\n\/\/\u521d\u59cb\u5316\u7ed3\u6784\u4f53\u4e2d\u7684\u53ef\u53d8\u53c2\u6570\n    info->var.rotate =         pdata->rotate;\n    info->var.xres =           width;\n    info->var.yres =           height;\n    info->var.xres_virtual =   info->var.xres;\n    info->var.yres_virtual =   info->var.yres;\n    info->var.bits_per_pixel = bpp;\n    info->var.nonstd =         1;\n\n    \/\/ \/* RGB565 *\/\/\/\u7ea2\u84dd\u53cd\u4e86\n    \/\/ info->var.red.offset =     0;\n    \/\/ info->var.red.length =     5;\n    \/\/ info->var.green.offset =   5;\n    \/\/ info->var.green.length =   6;\n    \/\/ info->var.blue.offset =    11;\n    \/\/ info->var.blue.length =    5;\n    \/\/ info->var.transp.offset =  0;\n    \/\/ info->var.transp.length =  0;\n\n    \/* RGB565 *\/\/\/\u7ea2\u84dd\u53cd\u4e86\n    info->var.red.offset =     11;\n    info->var.red.length =     5;\n    info->var.green.offset =   5;\n    info->var.green.length =   6;\n    info->var.blue.offset =    0;\n    info->var.blue.length =    5;\n    info->var.transp.offset =  0;\n    info->var.transp.length =  0;\n\n    info->flags =              FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB;\n\/\/\u521d\u59cb\u5316par\u7ed3\u6784\u4f53\u7684\u5404\u9879\u53c2\u6570\uff0c\u5982\u8c03\u8bd5\u6807\u5fd7\uff0c\u7f13\u51b2\u533a\u6307\u9488\uff0c\u65cb\u8f6c\u89d2\u5ea6\uff0cgamma\u66f2\u7ebf\n    par = info->par;\n    par->info = info;\n    par->pdata = pdata;\n    par->debug = display->debug;\n    par->buf = buf;\n    spin_lock_init(&par->dirty_lock);\n    \/\/par->bgr = pdata->bgr;\n    par->bgr = 1;\n    par->startbyte = pdata->startbyte;\n    par->init_sequence = init_sequence;\n    par->gamma.curves = gamma_curves;\n    par->gamma.num_curves = display->gamma_num;\n    par->gamma.num_values = display->gamma_len;\n    mutex_init(&par->gamma.lock);\n    info->pseudo_palette = par->pseudo_palette;\n\n    if (par->gamma.curves && gamma) {\n        if (fbtft_gamma_parse_str(par, par->gamma.curves, gamma,\n                      strlen(gamma)))\n            goto release_framebuf;\n    }\n\n    \/* Transmit buffer *\/\n    if (txbuflen == -1)\n        txbuflen = vmem_size + 2; \/* add in case startbyte is used *\/\n    if (txbuflen >= vmem_size + 2)\n        txbuflen = 0;\n\n#ifdef __LITTLE_ENDIAN\n    if ((!txbuflen) && (bpp > 8))\n        txbuflen = PAGE_SIZE; \/* need buffer for byteswapping *\/\n#endif\n\n    if (txbuflen > 0) {\n        txbuf = devm_kzalloc(par->info->device, txbuflen, GFP_KERNEL);\n        if (!txbuf)\n            goto release_framebuf;\n        par->txbuf.buf = txbuf;\n        par->txbuf.len = txbuflen;\n    }\n\n    \/* default fbtft operations *\/\n    \/\/\u521d\u59cb\u5316fbtftops\u7ed3\u6784\u4f53\uff0c\u5305\u62ec\u5199\u5165\u6570\u636e\u3001\u8bfb\u53d6\u6570\u636e\u3001\u66f4\u65b0\u663e\u793a\u7b49\u9ed8\u8ba4\u7684\u9a71\u52a8\u64cd\u4f5c\u51fd\u6570\n    par->fbtftops.write = fbtft_write_spi;\n    par->fbtftops.read = fbtft_read_spi;\n    par->fbtftops.write_vmem = fbtft_write_vmem16_bus8;\n    par->fbtftops.write_register = fbtft_write_reg8_bus8;\n    par->fbtftops.set_addr_win = fbtft_set_addr_win;\n    par->fbtftops.reset = fbtft_reset;\n    par->fbtftops.mkdirty = fbtft_mkdirty;\n    par->fbtftops.update_display = fbtft_update_display;\n    if (display->backlight)\n        par->fbtftops.register_backlight = fbtft_register_backlight;\n\n    \/* use driver provided functions *\/\n    fbtft_merge_fbtftops(&par->fbtftops, &display->fbtftops);\n\n    return info;\n\nrelease_framebuf:\n    framebuffer_release(info);\n\nalloc_fail:\n    vfree(vmem);\n\n    return NULL;\n}\n\n\/**\n * fbtft_framebuffer_release - frees up all memory used by the framebuffer\n * \u91ca\u653eframebuffer\u4f7f\u7528\u7684\u6240\u4ee5\u5185\u5b58\n *\n * @info: frame buffer info structure\n *\n *\/\nvoid fbtft_framebuffer_release(struct fb_info *info)\n{\n    fb_deferred_io_cleanup(info);\n    vfree(info->screen_buffer);\n    framebuffer_release(info);\n}\n\n\/**\n *  fbtft_register_framebuffer - registers a tft frame buffer device\n *  @fb_info: frame buffer info structure\n *\u6ce8\u518c\u4e00\u4e2a\u5e27\u7f13\u51b2\u8bbe\u5907\n *  Sets SPI driverdata if needed  \u5982\u679c\u9700\u8981\uff0c\u8bbe\u7f6espi\u9a71\u52a8\u7a0b\u5e8f\u6570\u636e\n *  Requests needed gpios.  \u6ce8\u518c\u9700\u8981\u7684gpio\n *  Initializes display  \u521d\u59cb\u5316\u663e\u793a\n *  Updates display.  \u66f4\u65b0\u663e\u793a\n *  Registers a frame buffer device @fb_info.\u6ce8\u518c\u4e00\u4e2a\u5e27\u7f13\u51b2\u8bbe\u5907\n *\n *  Returns negative errno on error, or zero for success.\n *\n *\/\nint fbtft_register_framebuffer(struct fb_info *fb_info)\n{\n    int ret;\n    char text1[50] = \"\";\n    char text2[50] = \"\";\n    struct fbtft_par *par = fb_info->par;\n    struct spi_device *spi = par->spi;\n\n    \/* sanity checks *\/  \/\/\u5b89\u5168\u68c0\u67e5\n    if (!par->fbtftops.init_display) {\n        dev_err(fb_info->device, \"missing fbtftops.init_display()\\n\");\n        return -EINVAL;\n    }\n\n    if (spi)\n        spi_set_drvdata(spi, fb_info);\n    if (par->pdev)\n        platform_set_drvdata(par->pdev, fb_info);\n\n    ret = par->fbtftops.request_gpios(par);\n    if (ret < 0)\n        goto reg_fail;\n\n    if (par->fbtftops.verify_gpios) {\n        ret = par->fbtftops.verify_gpios(par);\n        if (ret < 0)\n            goto reg_fail;\n    }\n\n    ret = par->fbtftops.init_display(par);\n    if (ret < 0)\n        goto reg_fail;\n    if (par->fbtftops.set_var) {\n        ret = par->fbtftops.set_var(par);\n        if (ret < 0)\n            goto reg_fail;\n    }\n\n    \/* update the entire display *\/\n    par->fbtftops.update_display(par, 0, par->info->var.yres - 1);\n\n    if (par->fbtftops.set_gamma && par->gamma.curves) {\n        ret = par->fbtftops.set_gamma(par, par->gamma.curves);\n        if (ret)\n            goto reg_fail;\n    }\n\n    if (par->fbtftops.register_backlight)\n        par->fbtftops.register_backlight(par);\n    printk(\"\u5339\u914d\u5df2\u7ecf\u6210\u529f\uff0c\u5f00\u59cb\u6ce8\u518cframebuffer\u51fd\u6570\uff0c\u751f\u6210\u8bbe\u5907\u53f7\\n\");\n    ret = register_framebuffer(fb_info); \/\/\u6ce8\u518cframebuffer\u9a71\u52a8\uff0c\u8fd9\u65f6\u7a7a\u95f4\u5df2\u7ecf\u5206\u914d\u5b8c\u6210\n    if (ret < 0)\n        goto reg_fail;\n    printk(\"\u751f\u6210\u8bbe\u5907\u53f7\u6210\u529f\\n\");\n    fbtft_sysfs_init(par);\n    printk(\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\\n\");\n    printk(\"\u6253\u5370\u8f93\u51fa\u7684bgr[%d]\\n\",par->bgr);\n    if (par->txbuf.buf && par->txbuf.len >= 1024)\n        sprintf(text1, \", %zu KiB buffer memory\", par->txbuf.len >> 10);\n    if (spi)\n        sprintf(text2, \", spi%d.%d at %d MHz\", spi->master->bus_num,\n            spi->chip_select, spi->max_speed_hz \/ 1000000);\n    dev_info(fb_info->dev,\n         \"%s frame buffer, %dx%d, %d KiB video memory%s, fps=%lu%s\\n\",\n         fb_info->fix.id, fb_info->var.xres, fb_info->var.yres,\n         fb_info->fix.smem_len >> 10, text1,\n         HZ \/ fb_info->fbdefio->delay, text2);\n\n    \/* Turn on backlight if available *\/\n    if (fb_info->bl_dev) {\n        fb_info->bl_dev->props.power = FB_BLANK_UNBLANK;\n        fb_info->bl_dev->ops->update_status(fb_info->bl_dev);\n    }\n\n    return 0;\n\nreg_fail:\n    if (par->fbtftops.unregister_backlight)\n        par->fbtftops.unregister_backlight(par);\n\n    return ret;\n}\n\n\/**\n *  fbtft_unregister_framebuffer - releases a tft frame buffer device\n \u91ca\u653etft\u5e27\u7f13\u51b2\u8bbe\u5907\n *  @fb_info: frame buffer info structure\n *\n *  Frees SPI driverdata if needed\n *  Frees gpios.\n *  Unregisters frame buffer device.\n *\n *\/\nint fbtft_unregister_framebuffer(struct fb_info *fb_info)\n{\n    struct fbtft_par *par = fb_info->par;\n\n    if (par->fbtftops.unregister_backlight)\n        par->fbtftops.unregister_backlight(par);\n    fbtft_sysfs_exit(par);\n    unregister_framebuffer(fb_info);\n\n    return 0;\n}\n\n\/**\n * fbtft_init_display_from_property() - Device Tree init_display() function\n * @par: Driver data\n * \u4ece\u8bbe\u5907\u6811\u5c5e\u6027\u521d\u59cb\u5316\u663e\u793a\u8bbe\u5907\n *\n * Return: 0 if successful, negative if error\n *\/\nstatic int fbtft_init_display_from_property(struct fbtft_par *par)\n{\n    struct device *dev = par->info->device;\n    int buf[64], count, index, i, j, ret;\n    u32 *values;\n    u32 val;\n\n    count = device_property_count_u32(dev, \"init\");\n    if (count < 0)\n        return count;\n    if (count == 0)\n        return -EINVAL;\n\n    values = kmalloc_array(count + 1, sizeof(*values), GFP_KERNEL);\n    if (!values)\n        return -ENOMEM;\n\n    ret = device_property_read_u32_array(dev, \"init\", values, count);\n    if (ret)\n        goto out_free;\n\n    par->fbtftops.reset(par);\n\n    index = -1;\n    val = values[++index];\n\n    while (index < count) {\n        if (val &#038; FBTFT_OF_INIT_CMD) {\n            val &#038;= 0xFFFF;\n            i = 0;\n            while ((index < count) &#038;&#038; !(val &#038; 0xFFFF0000)) {\n                if (i > 63) {\n                    dev_err(dev,\n                        \"%s: Maximum register values exceeded\\n\",\n                        __func__);\n                    ret = -EINVAL;\n                    goto out_free;\n                }\n                buf[i++] = val;\n                val = values[++index];\n            }\n            \/* make debug message *\/\n            fbtft_par_dbg(DEBUG_INIT_DISPLAY, par,\n                      \"init: write_register:\\n\");\n            for (j = 0; j < i; j++)\n                fbtft_par_dbg(DEBUG_INIT_DISPLAY, par,\n                          \"buf[%d] = %02X\\n\", j, buf[j]);\n\n            par->fbtftops.write_register(par, i,\n                buf[0], buf[1], buf[2], buf[3],\n                buf[4], buf[5], buf[6], buf[7],\n                buf[8], buf[9], buf[10], buf[11],\n                buf[12], buf[13], buf[14], buf[15],\n                buf[16], buf[17], buf[18], buf[19],\n                buf[20], buf[21], buf[22], buf[23],\n                buf[24], buf[25], buf[26], buf[27],\n                buf[28], buf[29], buf[30], buf[31],\n                buf[32], buf[33], buf[34], buf[35],\n                buf[36], buf[37], buf[38], buf[39],\n                buf[40], buf[41], buf[42], buf[43],\n                buf[44], buf[45], buf[46], buf[47],\n                buf[48], buf[49], buf[50], buf[51],\n                buf[52], buf[53], buf[54], buf[55],\n                buf[56], buf[57], buf[58], buf[59],\n                buf[60], buf[61], buf[62], buf[63]);\n        } else if (val & FBTFT_OF_INIT_DELAY) {\n            fbtft_par_dbg(DEBUG_INIT_DISPLAY, par,\n                      \"init: msleep(%u)\\n\", val & 0xFFFF);\n            msleep(val & 0xFFFF);\n            val = values[++index];\n        } else {\n            dev_err(dev, \"illegal init value 0x%X\\n\", val);\n            ret = -EINVAL;\n            goto out_free;\n        }\n    }\n\nout_free:\n    kfree(values);\n    return ret;\n}\n\n\/**\n * fbtft_init_display() - Generic init_display() function\n * \u901a\u7528\u7684\u663e\u793a\u8bbe\u5907\u521d\u59cb\u5316\u51fd\u6570\uff0c\u6839\u636e\u8bbe\u5907fbtft_par\u4e2d\u7684init_sequence\u6570\u7ec4\u6765\u6267\u884c\u521d\u59cb\u5316\u5e8f\u5217\n * @par: Driver data\n *\n * Uses par->init_sequence to do the initialization\n *\n * Return: 0 if successful, negative if error\n *\/\nint fbtft_init_display(struct fbtft_par *par)\n{\n    int buf[64];\n    char msg[128];\n    char str[16];\n    int i = 0;\n    int j;\n\n    \/* sanity check *\/\n    if (!par->init_sequence) {\n        dev_err(par->info->device,\n            \"error: init_sequence is not set\\n\");\n        return -EINVAL;\n    }\n\n    \/* make sure stop marker exists *\/\n    for (i = 0; i < FBTFT_MAX_INIT_SEQUENCE; i++)\n        if (par->init_sequence[i] == -3)\n            break;\n    if (i == FBTFT_MAX_INIT_SEQUENCE) {\n        dev_err(par->info->device,\n            \"missing stop marker at end of init sequence\\n\");\n        return -EINVAL;\n    }\n\n    par->fbtftops.reset(par);\n\n    i = 0;\n    while (i < FBTFT_MAX_INIT_SEQUENCE) {\n        if (par->init_sequence[i] == -3) {\n            \/* done *\/\n            return 0;\n        }\n        if (par->init_sequence[i] >= 0) {\n            dev_err(par->info->device,\n                \"missing delimiter at position %d\\n\", i);\n            return -EINVAL;\n        }\n        if (par->init_sequence[i + 1] < 0) {\n            dev_err(par->info->device,\n                \"missing value after delimiter %d at position %d\\n\",\n                par->init_sequence[i], i);\n            return -EINVAL;\n        }\n        switch (par->init_sequence[i]) {\n        case -1:\n            i++;\n            \/* make debug message *\/\n            strcpy(msg, \"\");\n            j = i + 1;\n            while (par->init_sequence[j] >= 0) {\n                sprintf(str, \"0x%02X \", par->init_sequence[j]);\n                strcat(msg, str);\n                j++;\n            }\n            fbtft_par_dbg(DEBUG_INIT_DISPLAY, par,\n                      \"init: write(0x%02X) %s\\n\",\n                      par->init_sequence[i], msg);\n\n            \/* Write *\/\n            j = 0;\n            while (par->init_sequence[i] >= 0) {\n                if (j > 63) {\n                    dev_err(par->info->device,\n                        \"%s: Maximum register values exceeded\\n\",\n                        __func__);\n                    return -EINVAL;\n                }\n                buf[j++] = par->init_sequence[i++];\n            }\n            par->fbtftops.write_register(par, j,\n                buf[0], buf[1], buf[2], buf[3],\n                buf[4], buf[5], buf[6], buf[7],\n                buf[8], buf[9], buf[10], buf[11],\n                buf[12], buf[13], buf[14], buf[15],\n                buf[16], buf[17], buf[18], buf[19],\n                buf[20], buf[21], buf[22], buf[23],\n                buf[24], buf[25], buf[26], buf[27],\n                buf[28], buf[29], buf[30], buf[31],\n                buf[32], buf[33], buf[34], buf[35],\n                buf[36], buf[37], buf[38], buf[39],\n                buf[40], buf[41], buf[42], buf[43],\n                buf[44], buf[45], buf[46], buf[47],\n                buf[48], buf[49], buf[50], buf[51],\n                buf[52], buf[53], buf[54], buf[55],\n                buf[56], buf[57], buf[58], buf[59],\n                buf[60], buf[61], buf[62], buf[63]);\n            break;\n        case -2:\n            i++;\n            fbtft_par_dbg(DEBUG_INIT_DISPLAY, par,\n                      \"init: mdelay(%d)\\n\",\n                      par->init_sequence[i]);\n            mdelay(par->init_sequence[i++]);\n            break;\n        default:\n            dev_err(par->info->device,\n                \"unknown delimiter %d at position %d\\n\",\n                par->init_sequence[i], i);\n            return -EINVAL;\n        }\n    }\n\n    dev_err(par->info->device,\n        \"%s: something is wrong. Shouldn't get here.\\n\", __func__);\n    return -EINVAL;\n}\n\n\/**\n * fbtft_verify_gpios() - Generic verify_gpios() function\n * \u901a\u7528\u521d\u59cb\u5316\u51fd\u6570\uff0c\u7528\u4e8e\u5e27\u7f13\u51b2\u8bbe\u5907\u9a71\u52a8\u4e2d\u7684\u8bbe\u5907\u521d\u59cb\u5316\u8fc7\u7a0b\n * @par: Driver data\n *\n * Uses @spi, @pdev and @buswidth to determine which GPIOs is needed\n *\n * Return: 0 if successful, negative if error\n *\/\nstatic int fbtft_verify_gpios(struct fbtft_par *par)\n{\n    struct fbtft_platform_data *pdata = par->pdata;\n    int i;\n\n    fbtft_par_dbg(DEBUG_VERIFY_GPIOS, par, \"%s()\\n\", __func__);\n\n    if (pdata->display.buswidth != 9 &&  par->startbyte == 0 &&\n        !par->gpio.dc) {\n        dev_err(par->info->device,\n            \"Missing info about 'dc' gpio. Aborting.\\n\");\n        return -EINVAL;\n    }\n\n    if (!par->pdev)\n        return 0;\n\n    if (!par->gpio.wr) {\n        dev_err(par->info->device, \"Missing 'wr' gpio. Aborting.\\n\");\n        return -EINVAL;\n    }\n    for (i = 0; i < pdata->display.buswidth; i++) {\n        if (!par->gpio.db[i]) {\n            dev_err(par->info->device,\n                \"Missing 'db%02d' gpio. Aborting.\\n\", i);\n            return -EINVAL;\n        }\n    }\n\n    return 0;\n}\n\n\/* returns 0 if the property is not present *\/\n\/\/\u4ece\u8bbe\u5907\u7684\u5c5e\u6027\u4e2d\u8bfb\u53d6\u4e00\u4e2a\u65e0\u7b26\u53f7\u768432\u4f4d\u6574\u6570\nstatic u32 fbtft_property_value(struct device *dev, const char *propname)\n{\n    int ret;\n    u32 val = 0;\n\n    ret = device_property_read_u32(dev, propname, &val);\n    if (ret == 0)\n        dev_info(dev, \"%s: %s = %u\\n\", __func__, propname, val);\n\n    return val;\n}\n\n\/\/\u4ece\u8bbe\u5907\u7684\u5c5e\u6027\u4e2d\u8bfb\u53d6\u4fe1\u606f\uff0c\u586b\u5145\u5230fbtft_platform_data\u7ed3\u6784\u4f53\u4e2d\nstatic struct fbtft_platform_data *fbtft_properties_read(struct device *dev)\n{\n    struct fbtft_platform_data *pdata;\n\n    if (!dev_fwnode(dev)) {\n        dev_err(dev, \"Missing platform data or properties\\n\");\n        return ERR_PTR(-EINVAL);\n    }\n\n    pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);\n    if (!pdata)\n        return ERR_PTR(-ENOMEM);\n\n    pdata->display.width = fbtft_property_value(dev, \"width\");\n    pdata->display.height = fbtft_property_value(dev, \"height\");\n    pdata->display.regwidth = fbtft_property_value(dev, \"regwidth\");\n    pdata->display.buswidth = fbtft_property_value(dev, \"buswidth\");\n    pdata->display.backlight = fbtft_property_value(dev, \"backlight\");\n    pdata->display.bpp = fbtft_property_value(dev, \"bpp\");\n    pdata->display.debug = fbtft_property_value(dev, \"debug\");\n    pdata->rotate = fbtft_property_value(dev, \"rotate\");\n    pdata->bgr = device_property_read_bool(dev, \"bgr\");\n    pdata->fps = fbtft_property_value(dev, \"fps\");\n    pdata->txbuflen = fbtft_property_value(dev, \"txbuflen\");\n    pdata->startbyte = fbtft_property_value(dev, \"startbyte\");\n    device_property_read_string(dev, \"gamma\", (const char **)&pdata->gamma);\n\n    if (device_property_present(dev, \"led-gpios\"))\n        pdata->display.backlight = 1;\n    if (device_property_present(dev, \"init\"))\n        pdata->display.fbtftops.init_display =\n            fbtft_init_display_from_property;\n\n    pdata->display.fbtftops.request_gpios = fbtft_request_gpios;\n\n    return pdata;\n}\n\n\/*****************************************************fbtft-core.c\u6587\u4ef6\u4e3b\u8981\u51fd\u6570\u7ed3\u675f********************************************************\/\n\n\/********************************************************fbtft-sysfs.c\u6587\u4ef6\u51fd\u6570\u5f00\u59cb**************************************************************\/\n\nstatic int get_next_ulong(char **str_p, unsigned long *val, char *sep, int base)\n{\n    char *p_val;\n\n    if (!str_p || !(*str_p))\n        return -EINVAL;\n\n    p_val = strsep(str_p, sep);\n\n    if (!p_val)\n        return -EINVAL;\n\n    return kstrtoul(p_val, base, val);\n}\n\nint fbtft_gamma_parse_str(struct fbtft_par *par, u32 *curves,\n              const char *str, int size)\n{\n    char *str_p, *curve_p = NULL;\n    char *tmp;\n    unsigned long val = 0;\n    int ret = 0;\n    int curve_counter, value_counter;\n    int _count;\n\n    fbtft_par_dbg(DEBUG_SYSFS, par, \"%s() str=\\n\", __func__);\n\n    if (!str || !curves)\n        return -EINVAL;\n\n    fbtft_par_dbg(DEBUG_SYSFS, par, \"%s\\n\", str);\n\n    tmp = kmemdup(str, size + 1, GFP_KERNEL);\n    if (!tmp)\n        return -ENOMEM;\n\n    \/* replace optional separators *\/\n    str_p = tmp;\n    while (*str_p) {\n        if (*str_p == ',')\n            *str_p = ' ';\n        if (*str_p == ';')\n            *str_p = '\\n';\n        str_p++;\n    }\n\n    str_p = strim(tmp);\n\n    curve_counter = 0;\n    while (str_p) {\n        if (curve_counter == par->gamma.num_curves) {\n            dev_err(par->info->device, \"Gamma: Too many curves\\n\");\n            ret = -EINVAL;\n            goto out;\n        }\n        curve_p = strsep(&str_p, \"\\n\");\n        value_counter = 0;\n        while (curve_p) {\n            if (value_counter == par->gamma.num_values) {\n                dev_err(par->info->device,\n                    \"Gamma: Too many values\\n\");\n                ret = -EINVAL;\n                goto out;\n            }\n            ret = get_next_ulong(&curve_p, &val, \" \", 16);\n            if (ret)\n                goto out;\n\n            _count = curve_counter * par->gamma.num_values +\n                 value_counter;\n            curves[_count] = val;\n            value_counter++;\n        }\n        if (value_counter != par->gamma.num_values) {\n            dev_err(par->info->device, \"Gamma: Too few values\\n\");\n            ret = -EINVAL;\n            goto out;\n        }\n        curve_counter++;\n    }\n    if (curve_counter != par->gamma.num_curves) {\n        dev_err(par->info->device, \"Gamma: Too few curves\\n\");\n        ret = -EINVAL;\n        goto out;\n    }\n\nout:\n    kfree(tmp);\n    return ret;\n}\n\nstatic ssize_t\nsprintf_gamma(struct fbtft_par *par, u32 *curves, char *buf)\n{\n    ssize_t len = 0;\n    unsigned int i, j;\n\n    mutex_lock(&par->gamma.lock);\n    for (i = 0; i < par->gamma.num_curves; i++) {\n        for (j = 0; j < par->gamma.num_values; j++)\n            len += scnprintf(&buf[len], PAGE_SIZE,\n                 \"%04x \", curves[i * par->gamma.num_values + j]);\n        buf[len - 1] = '\\n';\n    }\n    mutex_unlock(&par->gamma.lock);\n\n    return len;\n}\n\nstatic ssize_t store_gamma_curve(struct device *device,\n                 struct device_attribute *attr,\n                 const char *buf, size_t count)\n{\n    struct fb_info *fb_info = dev_get_drvdata(device);\n    struct fbtft_par *par = fb_info->par;\n    u32 tmp_curves[FBTFT_GAMMA_MAX_VALUES_TOTAL];\n    int ret;\n\n    ret = fbtft_gamma_parse_str(par, tmp_curves, buf, count);\n    if (ret)\n        return ret;\n\n    ret = par->fbtftops.set_gamma(par, tmp_curves);\n    if (ret)\n        return ret;\n\n    mutex_lock(&par->gamma.lock);\n    memcpy(par->gamma.curves, tmp_curves,\n           par->gamma.num_curves * par->gamma.num_values *\n           sizeof(tmp_curves[0]));\n    mutex_unlock(&par->gamma.lock);\n\n    return count;\n}\n\nstatic ssize_t show_gamma_curve(struct device *device,\n                struct device_attribute *attr, char *buf)\n{\n    struct fb_info *fb_info = dev_get_drvdata(device);\n    struct fbtft_par *par = fb_info->par;\n\n    return sprintf_gamma(par, par->gamma.curves, buf);\n}\n\nstatic struct device_attribute gamma_device_attrs[] = {\n    __ATTR(gamma, 0660, show_gamma_curve, store_gamma_curve),\n};\n\nvoid fbtft_expand_debug_value(unsigned long *debug)\n{\n    switch (*debug & 0x7) {\n    case 1:\n        *debug |= DEBUG_LEVEL_1;\n        break;\n    case 2:\n        *debug |= DEBUG_LEVEL_2;\n        break;\n    case 3:\n        *debug |= DEBUG_LEVEL_3;\n        break;\n    case 4:\n        *debug |= DEBUG_LEVEL_4;\n        break;\n    case 5:\n        *debug |= DEBUG_LEVEL_5;\n        break;\n    case 6:\n        *debug |= DEBUG_LEVEL_6;\n        break;\n    case 7:\n        *debug = 0xFFFFFFFF;\n        break;\n    }\n}\n\nstatic ssize_t store_debug(struct device *device,\n               struct device_attribute *attr,\n               const char *buf, size_t count)\n{\n    struct fb_info *fb_info = dev_get_drvdata(device);\n    struct fbtft_par *par = fb_info->par;\n    int ret;\n\n    ret = kstrtoul(buf, 10, &par->debug);\n    if (ret)\n        return ret;\n    fbtft_expand_debug_value(&par->debug);\n\n    return count;\n}\n\nstatic ssize_t show_debug(struct device *device,\n              struct device_attribute *attr, char *buf)\n{\n    struct fb_info *fb_info = dev_get_drvdata(device);\n    struct fbtft_par *par = fb_info->par;\n\n    return snprintf(buf, PAGE_SIZE, \"%lu\\n\", par->debug);\n}\n\nstatic struct device_attribute debug_device_attr =\n    __ATTR(debug, 0660, show_debug, store_debug);\n\nvoid fbtft_sysfs_init(struct fbtft_par *par)\n{\n    device_create_file(par->info->dev, &debug_device_attr);\n    if (par->gamma.curves && par->fbtftops.set_gamma)\n        device_create_file(par->info->dev, &gamma_device_attrs[0]);\n}\n\nvoid fbtft_sysfs_exit(struct fbtft_par *par)\n{\n    device_remove_file(par->info->dev, &debug_device_attr);\n    if (par->gamma.curves && par->fbtftops.set_gamma)\n        device_remove_file(par->info->dev, &gamma_device_attrs[0]);\n}\n\n\/********************************************************fbtft-sysfs.c\u6587\u4ef6\u51fd\u6570\u7ed3\u675f**************************************************************\/\n\n\/**********************************************************fbtft-bus.c\u6587\u4ef6\u7684\u5f00\u59cb*****************************************************************\/\n\n\/*****************************************************************************\n *\n *   void (*write_reg)(struct fbtft_par *par, int len, ...);\n *\n *****************************************************************************\/\n\ndefine_fbtft_write_reg(fbtft_write_reg8_bus8, u8, u8, )\ndefine_fbtft_write_reg(fbtft_write_reg16_bus8, __be16, u16, cpu_to_be16)\ndefine_fbtft_write_reg(fbtft_write_reg16_bus16, u16, u16, )\n\nvoid fbtft_write_reg8_bus9(struct fbtft_par *par, int len, ...)\n{\n    va_list args;\n    int i, ret;\n    int pad = 0;\n    u16 *buf = (u16 *)par->buf;\n\n    if (unlikely(par->debug & DEBUG_WRITE_REGISTER)) {\n        va_start(args, len);\n        for (i = 0; i < len; i++)\n            *(((u8 *)buf) + i) = (u8)va_arg(args, unsigned int);\n        va_end(args);\n        fbtft_par_dbg_hex(DEBUG_WRITE_REGISTER, par,\n                  par->info->device, u8, buf, len, \"%s: \",\n                  __func__);\n    }\n    if (len <= 0)\n        return;\n\n    if (par->spi && (par->spi->bits_per_word == 8)) {\n        \/* we're emulating 9-bit, pad start of buffer with no-ops\n         * (assuming here that zero is a no-op)\n         *\/\n        pad = (len % 4) ? 4 - (len % 4) : 0;\n        for (i = 0; i < pad; i++)\n            *buf++ = 0x000;\n    }\n\n    va_start(args, len);\n    *buf++ = (u8)va_arg(args, unsigned int);\n    i = len - 1;\n    while (i--) {\n        *buf = (u8)va_arg(args, unsigned int);\n        *buf++ |= 0x100; \/* dc=1 *\/\n    }\n    va_end(args);\n    ret = par->fbtftops.write(par, par->buf, (len + pad) * sizeof(u16));\n    if (ret < 0) {\n        dev_err(par->info->device,\n            \"write() failed and returned %d\\n\", ret);\n        return;\n    }\n}\n\n\/*****************************************************************************\n *\n *   int (*write_vmem)(struct fbtft_par *par);\n *\n *****************************************************************************\/\n\n\/* 16 bit pixel over 8-bit databus *\/\nint fbtft_write_vmem16_bus8(struct fbtft_par *par, size_t offset, size_t len)\n{\n    \/\/ u8 *vmem8;\n    \/\/ u8 *txbuf8 = par->txbuf.buf;\n    \/\/ size_t remain;\n    \/\/ size_t to_copy;\n    \/\/ size_t tx_array_size;\n    \/\/ int i;\n    \/\/ int ret = 0;\n    \/\/ size_t startbyte_size = 0;\n\n    \/\/ fbtft_par_dbg(DEBUG_WRITE_VMEM, par, \"%s(offset=%zu, len=%zu)\\n\",\n    \/\/        __func__, offset, len);\n\n    \/\/ remain = len;\n    \/\/ vmem8 = (u8 *)(par->info->screen_buffer + offset);\n\n    \/\/ gpiod_set_value(par->gpio.dc, 1);\n\n    \/\/ \/* non buffered write *\/\n    \/\/ if (!par->txbuf.buf)\n    \/\/  return par->fbtftops.write(par, vmem8, len);\n\n    \/\/ \/* buffered write *\/\n    \/\/ tx_array_size = par->txbuf.len;\n\n    \/\/ if (par->startbyte) {\n    \/\/  txbuf8 = par->txbuf.buf + 1;\n    \/\/  tx_array_size -= 1;\n    \/\/  *(u8 *)(par->txbuf.buf) = par->startbyte | 0x2;\n    \/\/  startbyte_size = 1;\n    \/\/ }\n\n    \/\/ while (remain) {\n    \/\/  to_copy = min(tx_array_size, remain);\n    \/\/  dev_dbg(par->info->device, \"to_copy=%zu, remain=%zu\\n\",\n    \/\/      to_copy, remain - to_copy);\n\n        \/\/ for (i = 0; i < to_copy; i++)\n        \/\/ {\n        \/\/  txbuf16[i] = cpu_to_be16(vmem16[i]);\n        \/\/ \/\/   printk(\"\u8f6c\u6362\u5b57\u8282\u5e8faaaaaaaaa\\n\");\n\n        \/\/ }\n\n    \/\/  \/\/ for (i = 0; i < to_copy; i++)\n    \/\/  \/\/ {\n    \/\/  \/\/  txbuf16[i] = be16_to_cpu(vmem16[i]);\n    \/\/  \/\/  printk(\"\u8f6c\u6362\u5b57\u8282\u5e8fbbbbbbbbbbbbb\\n\");\n    \/\/  \/\/ }\n\n    \/\/     for (i = 0; i < to_copy; i++)\n    \/\/  {\n    \/\/      \/\/txbuf16[i] = vmem16[i];\n    \/\/      txbuf8[i] = vmem8[i];         \/\/ R\n    \/\/         txbuf8[i + 1] = vmem8[i + 1]; \/\/ G\n    \/\/         txbuf8[i + 2] = vmem8[i + 2]; \/\/ B\n\n    \/\/  \/\/  printk(\"\u8f6c\u6362\u5b57\u8282\u5e8fcccccccccc\\n\");\n\n    \/\/  }\n\n    \/\/  vmem8 = vmem8 + to_copy;\n    \/\/  ret = par->fbtftops.write(par, par->txbuf.buf,\n    \/\/                  startbyte_size + to_copy);\n    \/\/  \/\/ ret = par->fbtftops.write(par, vmem8,\n    \/\/  \/\/              startbyte_size + to_copy);              \n    \/\/  if (ret < 0)\n    \/\/      return ret;\n    \/\/  remain -= to_copy;\n    \/\/}\n\n    u16 *vmem16;\n    __be16 *txbuf16 = par->txbuf.buf;\n    size_t remain;\n    size_t to_copy;\n    size_t tx_array_size;\n    int i;\n    int ret = 0;\n    size_t startbyte_size = 0;\n\n    fbtft_par_dbg(DEBUG_WRITE_VMEM, par, \"%s(offset=%zu, len=%zu)\\n\",\n              __func__, offset, len);\n\n    remain = len \/ 2;\n    vmem16 = (u16 *)(par->info->screen_buffer + offset);\n\n    gpiod_set_value(par->gpio.dc, 1);\n\n    \/* non buffered write *\/\n    if (!par->txbuf.buf)\n        return par->fbtftops.write(par, vmem16, len);\n\n    \/* buffered write *\/\n    tx_array_size = par->txbuf.len \/ 2;\n\n    if (par->startbyte) {\n        txbuf16 = par->txbuf.buf + 1;\n        tx_array_size -= 2;\n        *(u8 *)(par->txbuf.buf) = par->startbyte | 0x2;\n        startbyte_size = 1;\n    }\n\n    while (remain) {\n        to_copy = min(tx_array_size, remain);\n        dev_dbg(par->info->device, \"to_copy=%zu, remain=%zu\\n\",\n            to_copy, remain - to_copy);\n\n        for (i = 0; i < to_copy; i++)\n        {\n            txbuf16[i] = cpu_to_be16(vmem16[i]);\n\/\/            printk(\"\u8f6c\u6362\u5b57\u8282\u5e8faaaaaaaaa\\n\");\n\n        }\n\n        \/\/ for (i = 0; i < to_copy; i++)\n        \/\/ {\n        \/\/     txbuf16[i] = be16_to_cpu(vmem16[i]);\n        \/\/     printk(\"\u8f6c\u6362\u5b57\u8282\u5e8fbbbbbbbbbbbbb\\n\");\n        \/\/ }\n\n        \/\/ for (i = 0; i < to_copy; i++)\n        \/\/ {\n        \/\/     txbuf16[i] = vmem16[i];\n        \/\/ \/\/    printk(\"\u8f6c\u6362\u5b57\u8282\u5e8fcccccccccc\\n\");\n\n        \/\/ }\n\n        vmem16 = vmem16 + to_copy;\n        ret = par->fbtftops.write(par, par->txbuf.buf,\n                        startbyte_size + to_copy * 2);\n        if (ret < 0)\n            return ret;\n        remain -= to_copy;\n    }\n\n    return ret;\n}\n\n\/* 16 bit pixel over 9-bit SPI bus: dc + high byte, dc + low byte *\/\nint fbtft_write_vmem16_bus9(struct fbtft_par *par, size_t offset, size_t len)\n{\n    u8 *vmem8;\n    u16 *txbuf16 = par->txbuf.buf;\n    size_t remain;\n    size_t to_copy;\n    size_t tx_array_size;\n    int i;\n    int ret = 0;\n\n    fbtft_par_dbg(DEBUG_WRITE_VMEM, par, \"%s(offset=%zu, len=%zu)\\n\",\n              __func__, offset, len);\n\n    if (!par->txbuf.buf) {\n        dev_err(par->info->device, \"%s: txbuf.buf is NULL\\n\", __func__);\n        return -1;\n    }\n\n    remain = len;\n    vmem8 = par->info->screen_buffer + offset;\n\n    tx_array_size = par->txbuf.len \/ 2;\n\n    while (remain) {\n        to_copy = min(tx_array_size, remain);\n        dev_dbg(par->info->device, \"to_copy=%zu, remain=%zu\\n\",\n            to_copy, remain - to_copy);\n\n#ifdef __LITTLE_ENDIAN\n        for (i = 0; i < to_copy; i += 2) {\n            txbuf16[i]     = 0x0100 | vmem8[i + 1];\n            txbuf16[i + 1] = 0x0100 | vmem8[i];\n        }\n#else\n        for (i = 0; i < to_copy; i++)\n            txbuf16[i]   = 0x0100 | vmem8[i];\n#endif\n        vmem8 = vmem8 + to_copy;\n        ret = par->fbtftops.write(par, par->txbuf.buf, to_copy * 2);\n        if (ret < 0)\n            return ret;\n        remain -= to_copy;\n    }\n\n    return ret;\n}\n\nint fbtft_write_vmem8_bus8(struct fbtft_par *par, size_t offset, size_t len)\n{\n    dev_err(par->info->device, \"%s: function not implemented\\n\", __func__);\n    return -1;\n}\n\n\/* 16 bit pixel over 16-bit databus *\/\nint fbtft_write_vmem16_bus16(struct fbtft_par *par, size_t offset, size_t len)\n{\n    u16 *vmem16;\n\n    fbtft_par_dbg(DEBUG_WRITE_VMEM, par, \"%s(offset=%zu, len=%zu)\\n\",\n              __func__, offset, len);\n\n    vmem16 = (u16 *)(par->info->screen_buffer + offset);\n\n    \/* no need for buffered write with 16-bit bus *\/\n    return fbtft_write_buf_dc(par, vmem16, len, 1);\n}\n\n\/*********************************************************fbtft-bus.c\u6587\u4ef6\u7684\u7ed3\u675f****************************************************************\/\n\n\/************************************************************fbtft-io.c\u6587\u4ef6\u7684\u5f00\u59cb*****************************************************************\/\n\nint fbtft_write_spi(struct fbtft_par *par, void *buf, size_t len)\n{\n    struct spi_transfer t = {\n        .tx_buf = buf,\n        .len = len,\n    };\n    struct spi_message m;\n    \/\/ uint32_t *color_buf = (uint32_t *)buf;\n    \/\/ size_t num_colors = len; \/\/ \u6bcf\u4e2a\u989c\u8272\u503c\u662f16\u4f4d\uff082\u5b57\u8282\uff09\n    \/\/ size_t i = 0;\n\n    fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len,\n              \"%s(len=%zu): \", __func__, len);\n\n    if (!par->spi) {\n        dev_err(par->info->device,\n            \"%s: par->spi is unexpectedly NULL\\n\", __func__);\n        return -1;\n    }\n    \/\/ printk(\"aaaaaaaaaaaaaa\u6253\u5370\u8f93\u51fa\u7684\u989c\u8272p[%p]\\n\",buf);\n\n    \/\/ \/\/ \u6253\u5370\u989c\u8272\u6570\u636e\n    \/\/ printk(\"\u989c\u8272\u6570\u636e:\\n\");\n    \/\/ \/\/ for (i = 0; i < num_colors; i++) {\n    \/\/ printk(\"\u989c\u8272[%zu]: 0x%08x\\n\", i, color_buf[50]);\n    \/\/ printk(\"\u989c\u8272[%zu]: 0x%08x\\n\", i, color_buf[100]);\n    \/\/ printk(\"\u989c\u8272[%zu]: 0x%08x\\n\", i, color_buf[150]);\n    \/\/ printk(\"\u989c\u8272[%zu]: 0x%08x\\n\", i, color_buf[200]);\n    \/\/ printk(\"\u989c\u8272[%zu]: 0x%08x\\n\", i, color_buf[250]);\n    \/\/ printk(\"\u989c\u8272[%zu]: 0x%08x\\n\", i, color_buf[300]);\n    \/\/ printk(\"\u989c\u8272[%zu]: 0x%08x\\n\", i, color_buf[350]);\n    \/\/ printk(\"\u6253\u5370\u8f93\u51fa\u7684len[%d]\\n\",len);\n    \/\/ printk(\"\u6253\u5370\u8f93\u51fa\u7684bpp[%d]\\n\",par->info->var.bits_per_pixel);\n    \/\/ printk(\"\u6253\u5370\u8f93\u51fa\u7684bgr[%d]\\n\",par->bgr);\n    \/\/ printk(\"\u6253\u5370\u8f93\u51fa\u7684rotate[%d]\\n\",par->info->var.rotate);\n\n    \/\/ \/\/ }\n\n    spi_message_init(&m);\n    spi_message_add_tail(&t, &m);\n    return spi_sync(par->spi, &m);\n}\n\n\/**\n * fbtft_write_spi_emulate_9() - write SPI emulating 9-bit\n * @par: Driver data\n * @buf: Buffer to write\n * @len: Length of buffer (must be divisible by 8)\n *\n * When 9-bit SPI is not available, this function can be used to emulate that.\n * par->extra must hold a transformation buffer used for transfer.\n *\/\nint fbtft_write_spi_emulate_9(struct fbtft_par *par, void *buf, size_t len)\n{\n    u16 *src = buf;\n    u8 *dst = par->extra;\n    size_t size = len \/ 2;\n    size_t added = 0;\n    int bits, i, j;\n    u64 val, dc, tmp;\n\n    fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len,\n              \"%s(len=%zu): \", __func__, len);\n\n    if (!par->extra) {\n        dev_err(par->info->device, \"%s: error: par->extra is NULL\\n\",\n            __func__);\n        return -EINVAL;\n    }\n    if ((len % 8) != 0) {\n        dev_err(par->info->device,\n            \"error: len=%zu must be divisible by 8\\n\", len);\n        return -EINVAL;\n    }\n\n    for (i = 0; i < size; i += 8) {\n        tmp = 0;\n        bits = 63;\n        for (j = 0; j < 7; j++) {\n            dc = (*src &#038; 0x0100) ? 1 : 0;\n            val = *src &#038; 0x00FF;\n            tmp |= dc << bits;\n            bits -= 8;\n            tmp |= val << bits--;\n            src++;\n        }\n        tmp |= ((*src &#038; 0x0100) ? 1 : 0);\n        *(__be64 *)dst = cpu_to_be64(tmp);\n        dst += 8;\n        *dst++ = (u8)(*src++ &#038; 0x00FF);\n        added++;\n    }\n\n    return spi_write(par->spi, par->extra, size + added);\n}\n\nint fbtft_read_spi(struct fbtft_par *par, void *buf, size_t len)\n{\n    int ret;\n    u8 txbuf[32] = { 0, };\n    struct spi_transfer t = {\n            .speed_hz = 2000000,\n            .rx_buf     = buf,\n            .len        = len,\n        };\n    struct spi_message  m;\n\n    if (!par->spi) {\n        dev_err(par->info->device,\n            \"%s: par->spi is unexpectedly NULL\\n\", __func__);\n        return -ENODEV;\n    }\n\n    if (par->startbyte) {\n        if (len > 32) {\n            dev_err(par->info->device,\n                \"len=%zu can't be larger than 32 when using 'startbyte'\\n\",\n                len);\n            return -EINVAL;\n        }\n        txbuf[0] = par->startbyte | 0x3;\n        t.tx_buf = txbuf;\n        fbtft_par_dbg_hex(DEBUG_READ, par, par->info->device, u8,\n                  txbuf, len, \"%s(len=%zu) txbuf => \",\n                  __func__, len);\n    }\n\n    spi_message_init(&m);\n    spi_message_add_tail(&t, &m);\n    ret = spi_sync(par->spi, &m);\n    fbtft_par_dbg_hex(DEBUG_READ, par, par->info->device, u8, buf, len,\n              \"%s(len=%zu) buf <= \", __func__, len);\n\n    return ret;\n}\n\n\/*\n * Optimized use of gpiolib is twice as fast as no optimization\n * only one driver can use the optimized version at a time\n *\/\nint fbtft_write_gpio8_wr(struct fbtft_par *par, void *buf, size_t len)\n{\n    u8 data;\n    int i;\n#ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO\n    static u8 prev_data;\n#endif\n\n    fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len,\n              \"%s(len=%zu): \", __func__, len);\n\n    while (len--) {\n        data = *(u8 *)buf;\n\n        \/* Start writing by pulling down \/WR *\/\n        gpiod_set_value(par->gpio.wr, 1);\n\n        \/* Set data *\/\n#ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO\n        if (data == prev_data) {\n            gpiod_set_value(par->gpio.wr, 1); \/* used as delay *\/\n        } else {\n            for (i = 0; i < 8; i++) {\n                if ((data &#038; 1) != (prev_data &#038; 1))\n                    gpiod_set_value(par->gpio.db[i],\n                            data & 1);\n                data >>= 1;\n                prev_data >>= 1;\n            }\n        }\n#else\n        for (i = 0; i < 8; i++) {\n            gpiod_set_value(par->gpio.db[i], data & 1);\n            data >>= 1;\n        }\n#endif\n\n        \/* Pullup \/WR *\/\n        gpiod_set_value(par->gpio.wr, 0);\n\n#ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO\n        prev_data = *(u8 *)buf;\n#endif\n        buf++;\n    }\n\n    return 0;\n}\n\nint fbtft_write_gpio16_wr(struct fbtft_par *par, void *buf, size_t len)\n{\n    u16 data;\n    int i;\n#ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO\n    static u16 prev_data;\n#endif\n\n    fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len,\n              \"%s(len=%zu): \", __func__, len);\n\n    while (len) {\n        data = *(u16 *)buf;\n\n        \/* Start writing by pulling down \/WR *\/\n        gpiod_set_value(par->gpio.wr, 1);\n\n        \/* Set data *\/\n#ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO\n        if (data == prev_data) {\n            gpiod_set_value(par->gpio.wr, 1); \/* used as delay *\/\n        } else {\n            for (i = 0; i < 16; i++) {\n                if ((data &#038; 1) != (prev_data &#038; 1))\n                    gpiod_set_value(par->gpio.db[i],\n                            data & 1);\n                data >>= 1;\n                prev_data >>= 1;\n            }\n        }\n#else\n        for (i = 0; i < 16; i++) {\n            gpiod_set_value(par->gpio.db[i], data & 1);\n            data >>= 1;\n        }\n#endif\n\n        \/* Pullup \/WR *\/\n        gpiod_set_value(par->gpio.wr, 0);\n\n#ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO\n        prev_data = *(u16 *)buf;\n#endif\n        buf += 2;\n        len -= 2;\n    }\n\n    return 0;\n}\n\nint fbtft_write_gpio16_wr_latched(struct fbtft_par *par, void *buf, size_t len)\n{\n    dev_err(par->info->device, \"%s: function not implemented\\n\", __func__);\n    return -1;\n}\n\/*************************************************************fbtft-io.c\u6587\u4ef6\u7684\u7ed3\u675f***************************************************************\/\n\n\/***********************************************************st7789v\u4e3b\u6587\u4ef6\u5f97\u4e3b\u8981\u5185\u5bb9**************************************************************\/\n\n#define WIDTH       320\n#define HEIGHT      480\n\n\/* this init sequence matches PiScreen *\/\n\/\/\u8fd9\u4e2a\u662f\u4e00\u7ef4\u6570\u7ec4\uff0c\u4ee5\u8d1f\u6570\u4e3a\u6807\u5fd7\u4f4d\uff0c\u544a\u8bc9\u4e0b\u9762\u7684\u51fd\u6570\u8bfb\u53d6\u65f6\u8bfb\u7684\u662f\u4ec0\u4e48\nstatic const s16 default_init_sequence[] = {\n    \/* Interface Mode Control *\/  \/\/\u8bbe\u7f6espi\u4f20\u8f93\u6570\u636e\u7684\u6a21\u5f0f\n    -1, 0xb0, 0x0,\n    -1, MIPI_DCS_EXIT_SLEEP_MODE,  \/\/\u9000\u51fa\u7761\u7720\u6a21\u5f0f\n    -2, 250,\n    \/* Interface Pixel Format *\/  \/\/\u8bbe\u7f6espi\u50cf\u7d20\u683c\u5f0f\n    -1, MIPI_DCS_SET_PIXEL_FORMAT, 0x55,\n    \/* Power Control 3 *\/  \/\/\u529f\u7387\u63a7\u5236\n    -1, 0xC2, 0x44,  \/\/\u7535\u6e90\u6a21\u5f0f\uff0c\u6b63\u5e38\u6a21\u5f0f\n    -1, 0x21,  \/\/\u53cd\u663e\n    \/* VCOM Control 1 *\/  \/\/vcom\u63a7\u5236\n    -1, 0xC5, 0x00, 0x00, 0x00, 0x00,\n    \/\/-1, 0xC5, 0x00, 0x1e, 0x80,\n    \/\/-1, 0xb1, 0xb0,\/\/\u8bbe\u7f6e\u5168\u5f69\u6a21\u5f0f\u4e0b\u7684\u5e27\u7387\n    -1, 0xb6, 0x02,0x42,\n    -1, 0x36, 0x28,\n    \/* PGAMCTRL(Positive Gamma Control) *\/\n    -1, 0xE0, 0x00, 0x13, 0x18, 0x04, 0x0F, 0x06, 0x3a, 0x56,\n          0x4d, 0x03, 0x0a, 0x06, 0x30, 0x3e, 0x0f,\n    \/\/ -1, 0xE0, 0x00, 0x07, 0x10, 0x09, 0x17, 0x0b, 0x41, 0x89,\n    \/\/    0x4b, 0x0a, 0x0c, 0x0e, 0x18, 0x1b, 0x0f,       \n    \/* NGAMCTRL(Negative Gamma Control) *\/\n    -1, 0xE1, 0x00, 0x13, 0x18, 0x01, 0x11, 0x06, 0x38, 0x34,\n          0x4d, 0x06, 0x0d, 0x0b, 0x31, 0x37, 0x0f,\n    \/\/ -1, 0xE1, 0x00, 0x17, 0x1a, 0x04, 0x0e, 0x06, 0x2f, 0x45,\n    \/\/    0x43, 0x02, 0x0a, 0x09, 0x32, 0x36, 0x0f,\n    -1, MIPI_DCS_EXIT_SLEEP_MODE,\n    -1, MIPI_DCS_SET_DISPLAY_ON,\n    \/* end marker *\/\n    -3\n};\n\nstatic void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)\n{\n    write_reg(par, MIPI_DCS_SET_COLUMN_ADDRESS,\n          xs >> 8, xs & 0xFF, xe >> 8, xe & 0xFF);\n\n    write_reg(par, MIPI_DCS_SET_PAGE_ADDRESS,\n          ys >> 8, ys & 0xFF, ye >> 8, ye & 0xFF);\n\n    write_reg(par, MIPI_DCS_WRITE_MEMORY_START);\n}\n\nstatic int set_var(struct fbtft_par *par)\n{\n    switch (par->info->var.rotate) {\n    case 0:\n        write_reg(par, MIPI_DCS_SET_ADDRESS_MODE,\n              0x80 | (par->bgr << 3));\n        printk(\"\u8fdb\u5165\u8bbe\u7f6e\u89d2\u5ea6\u7684\u51fd\u6570\\n\");\n        break;\n    case 90:\n        write_reg(par, MIPI_DCS_SET_ADDRESS_MODE,\n              0x20 | (par->bgr << 3));\n        break;\n    case 180:\n        write_reg(par, MIPI_DCS_SET_ADDRESS_MODE,\n              0x40 | (par->bgr << 3));\n        break;\n    case 270:\n        write_reg(par, MIPI_DCS_SET_ADDRESS_MODE,\n              0xE0 | (par->bgr << 3));\n        break;\n    default:\n        break;\n    }\n\n    return 0;\n}\n\nstatic struct fbtft_display display = {\n    .regwidth = 8,\n    .width = WIDTH,\n    .height = HEIGHT,\n    .init_sequence = default_init_sequence,\n    .fbtftops = {\n        .set_addr_win = set_addr_win,\n        .set_var = set_var,\n    },\n};\n\nFBTFT_REGISTER_DRIVER(DRVNAME, \"ilitek,ili9488\", &#038;display);\/\/\u5339\u914d\u8bbe\u5907\u6811\u7684\u5b8f\n\/*********************************************************st7789v\u4e3b\u6587\u4ef6\u7684\u5185\u5bb9\u7ed3\u675f************************************************************************************\/\n\n\/**************************************************\u5339\u914d\u8bbe\u5907\u51fd\u6570\u5165\u53e3**********************************************************************************\/\n\/****************************prob\u548cremove\u5f97\u5177\u4f53\u5b9e\u73b0**********************************************\/\n\/**\n * fbtft_probe_common() - Generic device probe() helper function\n * @display: Display properties\n * @sdev: SPI device\n * @pdev: Platform device\n *\n * Allocates, initializes and registers a framebuffer\n *\n * Either @sdev or @pdev should be NULL\n *\n * Return: 0 if successful, negative if error\n *\/\n\/\/prob\u5165\u53e3\u521d\u59cb\u5316\u5b9e\u9645\u51fd\u6570\u5b9e\u73b0\nint fbtft_probe_common(struct fbtft_display *display,\n               struct spi_device *sdev,\n               struct platform_device *pdev)\n{\n    struct device *dev;\n    struct fb_info *info;\n    struct fbtft_par *par;\n    struct fbtft_platform_data *pdata;\n    int ret;\n\/\/\u5224\u65ad\u662fspi\u8bbe\u5907\u8fd8\u662f\u5e73\u53f0\u8bbe\u5907\u9a71\u52a8\n    if (sdev)\n        dev = &#038;sdev->dev;\n    else\n        dev = &pdev->dev;\n\n    if (unlikely(display->debug & DEBUG_DRIVER_INIT_FUNCTIONS))  \/\/define DEBUG_DRIVER_INIT_FUNCTIONS BIT(3)\uff0c\u610f\u601d\u662f\u5c061\u5de6\u79fb3\u4f4d\n        dev_info(dev, \"%s()\\n\", __func__);\n\n    pdata = dev->platform_data;\/\/\u7279\u5b9a\u7684\u663e\u793a\u6570\u636e\u7684\u7ed3\u6784\u4f53\n    if (!pdata) {\n        pdata = fbtft_properties_read(dev);\n        if (IS_ERR(pdata))\n            return PTR_ERR(pdata);\n    }\n\n    info = fbtft_framebuffer_alloc(display, dev, pdata); \/\/\u521b\u5efa\u4e00\u4e2a\u65b0\u7684\u5e27\u7f13\u51b2\u533a\u4fe1\u606f\u7ed3\u6784\uff0c\u5206\u914d\u7a7a\u95f4\u7b49\u64cd\u4f5c\n    if (!info)\n        return -ENOMEM;\n\n    par = info->par;\n    par->spi = sdev;\n    par->pdev = pdev;\n\n    if (display->buswidth == 0) {\n        dev_err(dev, \"buswidth is not set\\n\");\n        return -EINVAL;\n    }\n\n    \/* write register functions *\/  \/\/\u5199\u5bc4\u5b58\u5668\u51fd\u6570\uff0c\u5199\u6570\u636e\u6216\u547d\u4ee4\u7684\u51fd\u6570\n    if (display->regwidth == 8 && display->buswidth == 8)\n        par->fbtftops.write_register = fbtft_write_reg8_bus8;\n    else if (display->regwidth == 8 && display->buswidth == 9 && par->spi)\n        par->fbtftops.write_register = fbtft_write_reg8_bus9;\n    else if (display->regwidth == 16 && display->buswidth == 8)\n        par->fbtftops.write_register = fbtft_write_reg16_bus8;\n    else if (display->regwidth == 16 && display->buswidth == 16)\n        par->fbtftops.write_register = fbtft_write_reg16_bus16;\n    else\n        dev_warn(dev,\n             \"no default functions for regwidth=%d and buswidth=%d\\n\",\n             display->regwidth, display->buswidth);\n\n    \/* write_vmem() functions *\/  \/\/\u5199\u663e\u5b58\u5230\u663e\u793a\u5668\n    if (display->buswidth == 8)\n        par->fbtftops.write_vmem = fbtft_write_vmem16_bus8;\n    else if (display->buswidth == 9)\n        par->fbtftops.write_vmem = fbtft_write_vmem16_bus9;\n    else if (display->buswidth == 16)\n        par->fbtftops.write_vmem = fbtft_write_vmem16_bus16;\n\n    \/* GPIO write() functions *\/  \/\/gpio\u5199\u51fd\u6570\n    if (par->pdev) {\n        if (display->buswidth == 8)\n            par->fbtftops.write = fbtft_write_gpio8_wr;\n        else if (display->buswidth == 16)\n            par->fbtftops.write = fbtft_write_gpio16_wr;\n    }\n\n    \/* 9-bit SPI setup *\/  \/\/spi 9bit\u51fd\u6570\u7684\u542f\u52a8\n    if (par->spi && display->buswidth == 9) {\n        if (par->spi->master->bits_per_word_mask & SPI_BPW_MASK(9)) {\n            par->spi->bits_per_word = 9;\n        } else {\n            dev_warn(&par->spi->dev,\n                 \"9-bit SPI not available, emulating using 8-bit.\\n\");\n            \/* allocate buffer with room for dc bits *\/\n            par->extra = devm_kzalloc(par->info->device,\n                          par->txbuf.len +\n                          (par->txbuf.len \/ 8) + 8,\n                          GFP_KERNEL);\n            if (!par->extra) {\n                ret = -ENOMEM;\n                goto out_release;\n            }\n            par->fbtftops.write = fbtft_write_spi_emulate_9;\n        }\n    }\n\n    if (!par->fbtftops.verify_gpios)\n        par->fbtftops.verify_gpios = fbtft_verify_gpios;\n\n    \/* make sure we still use the driver provided functions *\/   \/\/\u786e\u4fdd\u6211\u4eec\u4ecd\u7136\u4f7f\u7528\u9a71\u52a8\u7a0b\u5e8f\u63d0\u4f9b\u7684\u51fd\u6570\n    fbtft_merge_fbtftops(&par->fbtftops, &display->fbtftops);\n\n    \/* use init_sequence if provided *\/ \/\/\u5982\u679c\u63d0\u4f9b\u7684\u8bdd\uff0c\u4f7f\u7528init sequence\n    if (par->init_sequence)\n        par->fbtftops.init_display = fbtft_init_display;\n\n    \/* use platform_data provided functions above all *\/  \/\/\u9996\u5148\u4f7f\u7528platform_data\u63d0\u4f9b\u7684\u51fd\u6570\n    fbtft_merge_fbtftops(&par->fbtftops, &pdata->display.fbtftops);\n\n    ret = fbtft_register_framebuffer(info);\n    if (ret < 0)\n        goto out_release;\n\n    return 0;\n\nout_release:\n    fbtft_framebuffer_release(info);\n\n    return ret;\n}\n\n\/**\n * fbtft_remove_common() - Generic device remove() helper function\n * @dev: Device\n * @info: Framebuffer\n *\n * Unregisters and releases the framebuffer\n *\n * Return: 0 if successful, negative if error\n *\/\n\n\/\/\u51fa\u53e3\u51fd\u6570\u5b9e\u9645\u5b9e\u73b0\nint fbtft_remove_common(struct device *dev, struct fb_info *info)\n{\n    struct fbtft_par *par;\n\n    if (!info)\n        return -EINVAL;\n    par = info->par;\n    if (par)\n        fbtft_par_dbg(DEBUG_DRIVER_INIT_FUNCTIONS, par,\n                  \"%s()\\n\", __func__);\n    fbtft_unregister_framebuffer(info);\n    fbtft_framebuffer_release(info);\n\n    return 0;\n}\n\n\/*********************prob\u548cremove\u5f97\u5177\u4f53\u5b9e\u73b0\u7ed3\u675f****************************************\/\n\n\/*****************************\u8bbe\u5907\u6811\u5339\u914d\u6a21\u5757*********************************************************\/\n\/\/\u8fd9\u90e8\u5206\u4ee3\u7801\u88ab\u5b8f\u4ee3\u66ff\uff0c\u6240\u4ee5\u6ce8\u91ca                                   \n\n\/**************************\u8bbe\u5907\u6811\u5339\u914d\u6a21\u5757\u7ed3\u675f*******************************************\/\n\/\/ module_init(fbtft_driver_module_init); \/\/\u5165\u53e3\u5b8f                                    \n\/\/ module_exit(fbtft_driver_module_exit); \/\/\u51fa\u53e3\u5b8f\n\n\/**************************************************\u5339\u914d\u8bbe\u5907\u5b8c\u6210**************************************************************************************\/\n\nMODULE_ALIAS(\"spi:\" DRVNAME);\nMODULE_ALIAS(\"platform:\" DRVNAME);\nMODULE_ALIAS(\"spi:ili9488\");\nMODULE_ALIAS(\"platform:ili9488\");\n\nMODULE_DESCRIPTION(\"FB driver for the ili9488 LCD Controller\");\nMODULE_AUTHOR(\"Dennis Menschel\");\nMODULE_LICENSE(\"GPL\");\n<\/code><\/pre>\n<h1>4 \u8bbe\u5907\u6811<\/h1>\n<h2>4.1 \u5173\u95ed\u8bbe\u5907\u8282\u70b9<\/h2>\n<p>&emsp;&emsp;\u8bbe\u5907\u6811\u4e5f\u662f\u4e00\u4e2a\u574e\uff0c\u5176\u4ed6\u90e8\u5206\u5185\u5bb9\u5728\u7f51\u4e0a\u90fd\u6709\u53c2\u8003\u4f9d\u636e\uff0c\u4ee3\u7801\u8c03\u8bd5\u8c03\u8bd5\u90fd\u80fd\u6539\u51fa\u6765\u3002\u4f46\u4e0b\u9762\u8fd9\u4e2a\u5173\u95edspidev\u8282\u70b9\u662f\u771f\u7684\u60f3\u4e0d\u5230\uff0c\u8fd9\u4e2a\u4e1c\u897f\u5982\u679c\u5b98\u65b9\u4e0d\u653e\u51fa\u6765\u7684\u8bdd\uff0c\u5f88\u96be\u81ea\u5df1\u641e\u51fa\u6765\uff1b\u56e0\u4e3a\u6839\u672c\u5c31\u548c\u8bbe\u5907\u6811\u4e0d\u642d\u8fb9\u554a\uff01\u8bbe\u7f6e\u5404\u79cd\u5c5e\u6027\u662f\u77e5\u9053\u7684\uff0c\u4f46\u5355\u72ec\u5173\u95ed\u4e00\u4e2aspi\u7684\u8282\u70b9\uff0c\u8fd9\u662f\u600e\u4e48\u80fd\u60f3\u5230\u7684\u5462\uff1f<\/p>\n<pre><code class=\"language-c\">spidev@0 {\n    status = \"disabled\";\n};<\/code><\/pre>\n<p>&emsp;&emsp;\u6b64\u5916\uff0c\u5982\u679c\u8fd8\u662f\u70b9\u4eae\u4e0d\u6210\u529f\uff0c\u53ef\u4ee5\u8003\u8651\u5728\u8bbe\u5907\u6811\u91cc\u52a0\u4e00\u4e0b\u8fd9\u4e2a\u53c2\u6570\uff0c\u7528\u4e8e\u8c03\u6574spi\u5f97\u65f6\u949f\u76f8\u4f4d\u548c\u65f6\u949f\u6781\u6027\u3002\u8fd9\u4e2a\u53c2\u6570\u662f\u6211\u5728\u5f00\u53d1\u677f\u7fa4\u91cc\u5411\u5176\u4ed6\u5927\u4f6c\u8bf7\u6559\u65f6\u83b7\u53d6\u5f97\u4e00\u7ec4\u53c2\u6570\uff0c\u9002\u7528\u7684\u5c4f\u5e55\u5e94\u8be5\u662fst7789v\u82af\u7247\u5c4f\u5e55\uff0c\u5177\u4f53\u60c5\u51b5\u8fd8\u662f\u8981\u770b\u5f00\u53d1\u624b\u518c\u3002<\/p>\n<pre><code class=\"language-c\">spi-cpol;\nspi-cpha;<\/code><\/pre>\n<h2>4.2 \u8bbe\u5907\u6811\u7f16\u5199<\/h2>\n<p>&emsp;&emsp;\u770b\u4e86\u534a\u5929\u611f\u89c9\u8bbe\u5907\u6811\u6ca1\u5565\u8bb2\u7684\uff0c\u5c31\u662f\u521d\u59cb\u5316\u51e0\u4e2a\u8282\u70b9\uff0c\u5177\u4f53\u914d\u7f6e\u5982\u4e0b\uff1a<\/p>\n<pre><code class=\"language-c\">&spi0 {\n    status = \"okay\";\n    pinctrl-names = \"default\";\n    pinctrl-0 = <&#038;spi0m0_pins &#038;tp_irq>;\n    \/\/ cs-gpios = <&#038;gpio1 RK_PC0 1>;\n    \/\/ cs-gpios = <&#038;gpio1 26 1>;\n    #address-cells = <1>;\n    #size-cells = <0>;\n    spidev@0 {\n              status = \"disabled\";\n      };\n   ili9488@0{\n       status = \"okay\";\n       compatible = \"ilitek,ili9488\";\n       reg = <0>;\n       spi-max-frequency = <20000000>;\n       fps = <30>;\n       bpp = <24>;\n       buswidth = <8>;\n       debug = <0x7>;\n       led-gpios = <&#038;gpio0 RK_PA4 GPIO_ACTIVE_LOW>;\/\/BL\n       dc = <&#038;gpio1 RK_PA2 GPIO_ACTIVE_HIGH>;      \/\/DC\n       reset = <&#038;gpio1 RK_PD1 GPIO_ACTIVE_LOW>;    \/\/RES-ili9488\n    \/\/   reset = <&#038;gpio1 RK_PC3 GPIO_ACTIVE_LOW>;    \/\/RES-st7789v\n   };\n\n};\n&pinctrl {\n    spi0 {\n        \/omit-if-no-ref\/\n        spi0m0_pins: spi0m0-pins {\n            rockchip,pins =\n                \/* spi0_clk_m0 *\/\n                <1 RK_PC1 4 &#038;pcfg_pull_none>,\n                \/* spie_miso_m0 *\/\n                <1 RK_PC3 6 &#038;pcfg_pull_none>, \n                \/* spi_mosi_m0 *\/\n                <1 RK_PC2 6 &#038;pcfg_pull_none>;\n        };\n    };\n};<\/code><\/pre>\n<p>&emsp;&emsp;\u6ce8\uff1a\u8fd8\u6709\u4e00\u90e8\u5206\u5f15\u811a\u521d\u59cb\u5316\u7684\u4ee3\u7801\u6211\u5c31\u4e0d\u653e\u4e86\uff0c\u90fd\u662f\u5343\u7bc7\u4e00\u5f8b\u7684\u4e1c\u897f\uff0c\u60f3\u4e86\u89e3\u7684\u770b\u4e0a\u9762spi\u7ae0\u8282\u7684\u622a\u56fe\uff0c\u81ea\u5df1\u7167\u846b\u82a6\u753b\u74e2\u4fee\u6539\u5c31\u884c\u4e86\u3002<\/p>\n<h2>4.3 \u5c0f\u7ed3<\/h2>\n<p>&emsp;&emsp;\u81f3\u6b64\uff0c\u6211\u4eec\u53ef\u4ee5\u770b\u51faframmebuffer\u9a71\u52a8\u6838\u5fc3\u5185\u5bb9\u4e5f\u5c31\u4e09\u5927\u6a21\u5757\uff0cframmmebuffer\u5b50\u7cfb\u7edf\u3001spi\u5b50\u7cfb\u7edf\u3001\u989c\u8272\u914d\u7f6e\uff0c\u641e\u61c2\u8fd9\u4e09\u5927\u5757\u5269\u4f59\u90e8\u5206\u90fd\u662f\u7ec6\u679d\u672b\u8282\uff0c\u6162\u6162\u8bfb\u90fd\u53ef\u4ee5\u770b\u61c2\u3002\u5176\u5b9e\u5f88\u60f3\u5410\u69fd\u5f97\u4e00\u70b9\u5c31\u662f\uff0c\u5b98\u65b9\u4e3a\u4e86\u517c\u5bb9\u6027\uff0c\u628a\u9a71\u52a8\u5199\u5f97\u8fc7\u4e8e\u5168\u9762\u548c\u590d\u6742\uff0c\u6211\u4e3a\u4e86\u65b9\u4fbf\u8bfb\u4ee3\u7801\uff0c\u4e13\u95e8\u628a\u5b98\u65b9\u7684\u9a71\u52a8\u6574\u5408\u5230\u4e86\u4e00\u4e2a.c\u6587\u4ef6\u4e2d\uff0c\u6574\u7406\u8fc7\u7a0b\u4e2d\u53d1\u73b0\u5f88\u591a\u51fd\u6570\u548c\u4ee3\u7801\u538b\u6839\u5c31\u6ca1\u88ab\u8c03\u7528\uff0c\u7cbe\u7b80\u5b8c\u4ee5\u540e\uff0c\u4ee3\u7801\u5c11\u4e86\u5f88\u5927\u4e00\u5757\u3002<\/p>\n<h1>5 drm\u7684\u7b80\u4ecb<\/h1>\n<p>&emsp;&emsp;\u6211\u56e0\u4e3a\u5c4f\u5e55\u9650\u5236\uff0c\u6ca1\u641edrm\u8fd9\u4e48\u9ad8\u7ea7\u7684\u9a71\u52a8\uff0c\u5c31\u67e5\u8be2\u6574\u7406\u4e86\u4e00\u4e9b\u8d44\u6599\u5206\u4eab\u7ed9\u5927\u5bb6\u3002<br \/>\n&emsp;&emsp;DRM\uff08Direct Rendering Manager\uff09\u9a71\u52a8\u548cFramebuffer\u9a71\u52a8\u662fLinux\u64cd\u4f5c\u7cfb\u7edf\u4e2d\u7528\u4e8e\u7ba1\u7406\u56fe\u5f62\u786c\u4ef6\u7684\u4e24\u79cd\u4e0d\u540c\u7684\u56fe\u5f62\u5b50\u7cfb\u7edf\uff0c\u5b83\u4eec\u5728\u67b6\u6784\u548c\u529f\u80fd\u4e0a\u5b58\u5728\u663e\u8457\u7684\u5dee\u5f02\u3002<\/p>\n<h2>5.1 \u8bbe\u8ba1\u76ee\u6807<\/h2>\n<p>&emsp;&emsp;Framebuffer \u9a71\u52a8\uff1a<\/p>\n<pre><code class=\"language-c\">\u6700\u521d\u8bbe\u8ba1\u7528\u4e8e\u7b80\u5355\u7684\u56fe\u5f62\u8f93\u51fa\u8bbe\u5907\u3002\n\u76ee\u6807\u662f\u63d0\u4f9b\u57fa\u672c\u7684\u3001\u76f4\u63a5\u7684\u5e27\u7f13\u51b2\u8bbf\u95ee\u63a5\u53e3\uff0c\u5141\u8bb8\u7528\u6237\u7a7a\u95f4\u5e94\u7528\u7a0b\u5e8f\u76f4\u63a5\u5199\u5165\u663e\u5b58\uff0c\u7ed8\u5236\u56fe\u50cf\u3002\n\u5b83\u4e0d\u652f\u6301\u73b0\u4ee3\u56fe\u5f62\u786c\u4ef6\u7684\u9ad8\u7ea7\u7279\u6027\uff0c\u6bd4\u5982\u786c\u4ef6\u52a0\u901f\u3001\u663e\u793a\u7ba1\u9053\u7ba1\u7406\u3001VSync\u3001\u53cc\u7f13\u51b2\u7b49\u3002\n\u9002\u7528\u4e8e\u7b80\u5355\u7684\u5d4c\u5165\u5f0f\u7cfb\u7edf\u548c\u7ec8\u7aef\u8bbe\u5907\uff0c\u6027\u80fd\u9700\u6c42\u8f83\u4f4e\u3002<\/code><\/pre>\n<p>&emsp;&emsp;DRM \u9a71\u52a8\uff1a<\/p>\n<pre><code class=\"language-c\">\u8bbe\u8ba1\u76ee\u6807\u662f\u652f\u6301\u73b0\u4ee3 GPU \u548c\u590d\u6742\u7684\u56fe\u5f62\u786c\u4ef6\uff0c\u4e3b\u8981\u7528\u4e8e\u684c\u9762\u7cfb\u7edf\u3001\u56fe\u5f62\u754c\u9762\u548c\u9700\u8981\u786c\u4ef6\u52a0\u901f\u7684\u5e94\u7528\u7a0b\u5e8f\uff08\u4f8b\u5982\u6e38\u620f\u3001\u56fe\u5f62\u5bc6\u96c6\u578b\u5e94\u7528\uff09\u3002\n\u652f\u6301\u786c\u4ef6\u52a0\u901f\u3001\u6e32\u67d3\u3001\u591a\u663e\u793a\u5668\u3001\u53cc\u7f13\u51b2\u7b49\u9ad8\u7ea7\u529f\u80fd\u3002\nDRM \u9a71\u52a8\u4e0e KMS\uff08Kernel Mode Setting\uff0c\u5185\u6838\u6a21\u5f0f\u8bbe\u7f6e\uff09\u7d27\u5bc6\u7ed3\u5408\uff0c\u5141\u8bb8\u5185\u6838\u63a7\u5236\u663e\u793a\u6a21\u5f0f\u3001\u8fde\u63a5\u5668\u7b49\u663e\u793a\u8d44\u6e90\u7ba1\u7406\u3002<\/code><\/pre>\n<h2>5.2 \u67b6\u6784<\/h2>\n<p>&emsp;&emsp;Framebuffer \u9a71\u52a8\uff1a<\/p>\n<pre><code class=\"language-c\">\u7b80\u5355\u7684\u67b6\u6784\uff0c\u6838\u5fc3\u662f\u901a\u8fc7 \/dev\/fb0 \u7b49\u8bbe\u5907\u8282\u70b9\u4e0e\u663e\u5b58\u76f4\u63a5\u4ea4\u4e92\u3002\n\u901a\u8fc7 mmap \u6620\u5c04\u663e\u5b58\uff0c\u7528\u6237\u7a7a\u95f4\u5e94\u7528\u53ef\u4ee5\u76f4\u63a5\u64cd\u4f5c\u663e\u5b58\u4e2d\u7684\u6bcf\u4e2a\u50cf\u7d20\u3002\n\u6ca1\u6709 GPU \u786c\u4ef6\u52a0\u901f\u652f\u6301\uff0c\u4e5f\u4e0d\u5904\u7406\u590d\u6742\u7684\u663e\u793a\u4efb\u52a1\u3002\n\u663e\u793a\u7684\u5206\u8fa8\u7387\u548c\u989c\u8272\u6df1\u5ea6\u7b49\u8bbe\u7f6e\u5728\u9a71\u52a8\u52a0\u8f7d\u65f6\u5b8c\u6210\uff0c\u65e0\u6cd5\u52a8\u6001\u5207\u6362\u3002<\/code><\/pre>\n<p>&emsp;&emsp;DRM \u9a71\u52a8\uff1a<\/p>\n<pre><code class=\"language-c\">DRM \u9a71\u52a8\u662f Linux \u5185\u6838\u4e2d\u7684\u4e00\u4e2a\u5b50\u7cfb\u7edf\uff0c\u5305\u542b\u4e24\u4e2a\u4e3b\u8981\u90e8\u5206\uff1a\u6e32\u67d3\u548c\u663e\u793a\u3002\nDRM \u901a\u8fc7\u4e0e GPU \u786c\u4ef6\u7684\u4ea4\u4e92\uff0c\u5b9e\u73b0\u786c\u4ef6\u52a0\u901f\u6e32\u67d3\uff0c\u540c\u65f6\u7ba1\u7406\u663e\u793a\u8f93\u51fa\u3002\nKMS \u662f DRM \u7684\u4e00\u90e8\u5206\uff0c\u7528\u4e8e\u7ba1\u7406\u663e\u793a\u7ba1\u9053\uff0c\u8bbe\u7f6e\u663e\u793a\u6a21\u5f0f\uff08\u5982\u5206\u8fa8\u7387\u3001\u5237\u65b0\u7387\uff09\u7b49\u3002\nDRM \u9a71\u52a8\u901a\u8fc7\u4e0e\u7528\u6237\u7a7a\u95f4\u56fe\u5f62\u6808\uff08\u5982 Mesa\u3001Xorg \u6216 Wayland\uff09\u534f\u4f5c\uff0c\u63d0\u4f9b\u590d\u6742\u7684\u56fe\u5f62\u64cd\u4f5c\u548c\u6e32\u67d3\u652f\u6301\u3002\n\u652f\u6301\u591a\u7f13\u51b2\uff08\u5982\u53cc\u7f13\u51b2\u3001\u4e09\u7f13\u51b2\uff09\u548c VSync\uff0c\u4ee5\u786e\u4fdd\u5e73\u6ed1\u7684\u56fe\u50cf\u66f4\u65b0\u3002<\/code><\/pre>\n<h2>5.3 \u529f\u80fd\u548c\u6027\u80fd<\/h2>\n<p>&emsp;&emsp;Framebuffer \u9a71\u52a8\uff1a<\/p>\n<pre><code class=\"language-c\">\u529f\u80fd\u7b80\u5355\uff0c\u9002\u5408\u76f4\u63a5\u8bbf\u95ee\u663e\u5b58\u8fdb\u884c\u50cf\u7d20\u7ea7\u522b\u7684\u7ed8\u5236\u64cd\u4f5c\u3002\n\u4e0d\u652f\u6301\u786c\u4ef6\u52a0\u901f\uff0c\u6240\u6709\u7684\u56fe\u5f62\u64cd\u4f5c\uff08\u5982\u56fe\u5f62\u7ed8\u5236\u3001\u7a97\u53e3\u7ba1\u7406\u7b49\uff09\u90fd\u4f9d\u8d56\u4e8e CPU\uff0c\u8fd9\u5bf9\u4e8e\u590d\u6742\u7684\u56fe\u5f62\u5e94\u7528\u7a0b\u5e8f\u6027\u80fd\u8f83\u5dee\u3002\n\u4e0d\u5177\u5907\u591a\u663e\u793a\u5668\u7ba1\u7406\u3001\u786c\u4ef6\u52a0\u901f\u6e32\u67d3\u7b49\u529f\u80fd\u3002\n\u5178\u578b\u4f7f\u7528\u573a\u666f\uff1a\u5d4c\u5165\u5f0f\u7cfb\u7edf\u4e2d\u7684\u63a7\u5236\u53f0\u3001\u7b80\u5355\u56fe\u5f62\u5e94\u7528\u3002<\/code><\/pre>\n<p>&emsp;&emsp;DRM \u9a71\u52a8\uff1a<\/p>\n<pre><code class=\"language-c\">\u652f\u6301 GPU \u52a0\u901f\uff0c\u53ef\u4ee5\u4f7f\u7528 GPU \u6765\u6267\u884c\u590d\u6742\u7684\u56fe\u5f62\u8ba1\u7b97\u4efb\u52a1\uff0c\u5982 3D \u6e32\u67d3\u3001\u56fe\u50cf\u5904\u7406\u7b49\u3002\n\u5177\u5907\u591a\u663e\u793a\u5668\u652f\u6301\uff0c\u53ef\u4ee5\u901a\u8fc7\u5185\u6838\u52a8\u6001\u8c03\u6574\u663e\u793a\u5668\u7684\u5206\u8fa8\u7387\u3001\u5237\u65b0\u7387\u3001\u989c\u8272\u683c\u5f0f\u7b49\u3002\n\u901a\u8fc7 VSync \u540c\u6b65\uff0c\u9632\u6b62\u56fe\u50cf\u6495\u88c2\uff0c\u652f\u6301\u66f4\u9ad8\u7684\u5e27\u7387\u548c\u663e\u793a\u6548\u679c\u3002\n\u5178\u578b\u4f7f\u7528\u573a\u666f\uff1a\u684c\u9762\u73af\u5883\u3001\u6e38\u620f\u30013D \u56fe\u5f62\u5e94\u7528\u7b49\u3002<\/code><\/pre>\n<h2>5.4 \u7528\u6237\u7a7a\u95f4\u63a5\u53e3<\/h2>\n<p>&emsp;&emsp;Framebuffer \u9a71\u52a8\uff1a<\/p>\n<pre><code class=\"language-c\">\u63d0\u4f9b\u7b80\u5355\u7684\u7528\u6237\u7a7a\u95f4\u63a5\u53e3 \/dev\/fbX\uff0c\u53ef\u4ee5\u901a\u8fc7\u6587\u4ef6\u8bfb\u5199\u548c ioctl \u8c03\u7528\u4e0e\u9a71\u52a8\u8fdb\u884c\u4ea4\u4e92\u3002\n\u4e3b\u8981\u7528\u4e8e\u7ed8\u5236\u50cf\u7d20\u6570\u636e\uff0c\u4f46\u4e0d\u652f\u6301\u590d\u6742\u7684 3D \u6e32\u67d3\u3002<\/code><\/pre>\n<p>&emsp;&emsp;DRM \u9a71\u52a8\uff1a<\/p>\n<pre><code class=\"language-c\">\u901a\u8fc7 \/dev\/dri\/cardX \u63d0\u4f9b\u63a5\u53e3\uff0c\u5141\u8bb8\u7528\u6237\u7a7a\u95f4\u901a\u8fc7 DRM IOCTL \u4e0e GPU \u9a71\u52a8\u8fdb\u884c\u4ea4\u4e92\u3002\n\u652f\u6301\u7528\u6237\u7a7a\u95f4\u7684\u56fe\u5f62\u5e93\uff0c\u5982 Mesa3D\u3001X11\u3001Wayland\u3001EGL \u7b49\u3002\n\u73b0\u4ee3\u56fe\u5f62\u6808\uff08\u5982 Xorg\u3001Wayland\uff09\u90fd\u4f9d\u8d56 DRM \u9a71\u52a8\u6765\u63d0\u4f9b\u786c\u4ef6\u52a0\u901f\u652f\u6301\u3002<\/code><\/pre>\n<h2>5.5 \u663e\u793a\u7ba1\u7406<\/h2>\n<p>&emsp;&emsp;Framebuffer \u9a71\u52a8\uff1a<\/p>\n<pre><code class=\"language-c\">\u4f9d\u8d56\u4e8e\u56fa\u5b9a\u5206\u8fa8\u7387\u7684\u6a21\u5f0f\u8bbe\u7f6e\uff0c\u96be\u4ee5\u52a8\u6001\u8c03\u6574\u663e\u793a\u8f93\u51fa\u3002\n\u7f3a\u4e4f\u591a\u663e\u793a\u5668\u652f\u6301\uff0c\u4e0d\u80fd\u540c\u65f6\u7ba1\u7406\u591a\u4e2a\u663e\u793a\u8bbe\u5907\u3002<\/code><\/pre>\n<p>&emsp;&emsp;DRM \u9a71\u52a8\uff1a<\/p>\n<pre><code class=\"language-c\">\u901a\u8fc7 KMS \u53ef\u4ee5\u52a8\u6001\u8bbe\u7f6e\u548c\u7ba1\u7406\u663e\u793a\u6a21\u5f0f\uff0c\u5982\u5206\u8fa8\u7387\u3001\u5237\u65b0\u7387\u7b49\u3002\n\u5177\u5907\u591a\u663e\u793a\u5668\u652f\u6301\uff0c\u80fd\u591f\u7ba1\u7406\u591a\u4e2a\u663e\u793a\u8f93\u51fa\uff08\u5982 HDMI\u3001DisplayPort\u3001VGA \u7b49\uff09\u3002<\/code><\/pre>\n<h2>5.6 \u4f7f\u7528\u573a\u666f<\/h2>\n<p>&emsp;&emsp;Framebuffer \u9a71\u52a8\uff1a<\/p>\n<pre><code class=\"language-c\">\u7b80\u5355\u56fe\u5f62\u8f93\u51fa\uff0c\u4e0d\u9700\u8981 GPU \u52a0\u901f\u7684\u5e94\u7528\u3002\n\u4e3b\u8981\u7528\u4e8e\u5d4c\u5165\u5f0f\u8bbe\u5907\u6216\u4e0d\u9700\u8981\u590d\u6742\u56fe\u5f62\u64cd\u4f5c\u7684\u7cfb\u7edf\u3002\n\u7ec8\u7aef\u63a7\u5236\u53f0\u3001\u65e9\u671f\u7684 Linux \u7cfb\u7edf\u63a7\u5236\u53f0\u3001\u4e00\u4e9b\u5d4c\u5165\u5f0f\u7cfb\u7edf\u3002<\/code><\/pre>\n<p>&emsp;&emsp;DRM \u9a71\u52a8\uff1a<\/p>\n<pre><code class=\"language-c\">\u684c\u9762\u7cfb\u7edf\uff08\u5982 GNOME\u3001KDE\uff09\u4f7f\u7528 DRM \u9a71\u52a8\u6765\u63d0\u4f9b\u56fe\u5f62\u52a0\u901f\u3002\n\u6e38\u620f\u3001\u89c6\u9891\u64ad\u653e\u548c\u9700\u8981\u9ad8\u6027\u80fd\u6e32\u67d3\u7684\u5e94\u7528\u7a0b\u5e8f\u90fd\u4f9d\u8d56\u4e8e DRM \u9a71\u52a8\u3002\n\u73b0\u4ee3\u684c\u9762\u7cfb\u7edf\u7684\u663e\u793a\u670d\u52a1\u5668\uff08\u5982 Wayland\u3001Xorg\uff09\u901a\u8fc7 DRM \u9a71\u52a8\u6765\u63a7\u5236\u663e\u793a\u8f93\u51fa\u548c\u6e32\u67d3\u64cd\u4f5c\u3002<\/code><\/pre>\n<h2>5.7 \u5c0f\u7ed3<\/h2>\n<p>&emsp;&emsp;Framebuffer \u662f\u4e00\u79cd\u8001\u65e7\u7684\u663e\u793a\u7ba1\u7406\u65b9\u5f0f\uff0c\u4e3b\u8981\u63d0\u4f9b\u76f4\u63a5\u7684\u663e\u5b58\u8bbf\u95ee\uff0c\u4e0d\u652f\u6301\u786c\u4ef6\u52a0\u901f\uff0c\u529f\u80fd\u8f83\u4e3a\u7b80\u5355\u3002<br \/>\nDRM \u5219\u662f\u73b0\u4ee3 Linux \u56fe\u5f62\u6808\u7684\u91cd\u8981\u90e8\u5206\uff0c\u652f\u6301 GPU \u786c\u4ef6\u52a0\u901f\u548c\u590d\u6742\u7684\u663e\u793a\u7ba1\u7406\uff0c\u662f\u5f53\u524d\u684c\u9762\u7cfb\u7edf\u548c\u56fe\u5f62\u5e94\u7528\u7684\u6838\u5fc3\u7ec4\u4ef6\u3002<\/p>\n<h1>6 \u603b\u7ed3<\/h1>\n<p>&emsp;&emsp;\u81f3\u6b64\uff0clcd\u5c4f\u5e55\u7684\u663e\u793a\u90e8\u5206\u9a71\u52a8\u5c31\u79fb\u690d\u5b8c\u6210\u4e86\uff0c\u8fd9\u4e2a\u6587\u7ae0\u7684\u7740\u91cd\u70b9\u5176\u5b9e\u5c31\u662f\u6211\u5728\u79fb\u690d\u5c4f\u5e55\u65f6\u9047\u5230\u7684\u95ee\u9898\u3002\u6574\u4f53\u79fb\u690d\u5b8c\u6210\u56de\u6765\u770b\u65f6\uff0c\u6211\u89c9\u5f97framebuffer\u5b50\u7cfb\u7edf\u6ca1\u5565\u592a\u96be\u7684\u90e8\u5206\uff1b\u90fd\u662f\u56e0\u4e3a\u4e0d\u719f\u6089\u9a71\u52a8\u6846\u67b6\uff0c\u624d\u5bfc\u81f4\u6211\u4e0d\u65ad\u7684\u8e0f\u8fdb\u5404\u79cd\u5751\u91cc\u53c8\u4e0d\u65ad\u5730\u722c\u51fa\u6765\u3002\u503c\u5f97\u5e86\u5e78\u7684\u662f\u6700\u7ec8\u6211\u5b8c\u6210\u4e86\u9a71\u52a8\u5730\u79fb\u690d\u5de5\u4f5c\u3002<br \/>\n&emsp;&emsp;\u6700\u540e\uff0c\u8fd9\u4e2a\u662f\u6211\u4e2a\u4eba\u5728\u79fb\u690d\u5c4f\u5e55\u65f6\u9047\u5230\u5730\u95ee\u9898\uff0c\u5982\u4f55\u9605\u8bfb\u6587\u7ae0\u5730\u5404\u4f4d\u9047\u5230\u5176\u4ed6\u95ee\u9898\u6b22\u8fce\u8bc4\u8bba\u533a\u4e00\u8d77\u8ba8\u8bba\uff01\uff01\uff01\uff01<\/p>\n","protected":false},"excerpt":{"rendered":"<p>1 \u524d\u8a00 &emsp;&emsp;\u5173\u4e8eframebuffer\u7684\u79fb\u690d\uff0c\u8fd9\u662f\u6211\u7b2c\u4e00\u6b21\u63a5\u89e6\u7a0d\u5fae\u9ad8\u7aef\u70b9\u7684\u9a71\u52a8\u3002\u4ee5\u524d\u90fd\u662f [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":503,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[15,12,10,14,13,16,11],"class_list":["post-451","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-flushbonading-lcd","tag-drm","tag-frammebuffer","tag-lcd","tag-mmap","tag-spi","tag-16","tag-11"],"_links":{"self":[{"href":"https:\/\/www.yanwenkai.com\/index.php?rest_route=\/wp\/v2\/posts\/451","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.yanwenkai.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.yanwenkai.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.yanwenkai.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.yanwenkai.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=451"}],"version-history":[{"count":48,"href":"https:\/\/www.yanwenkai.com\/index.php?rest_route=\/wp\/v2\/posts\/451\/revisions"}],"predecessor-version":[{"id":756,"href":"https:\/\/www.yanwenkai.com\/index.php?rest_route=\/wp\/v2\/posts\/451\/revisions\/756"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.yanwenkai.com\/index.php?rest_route=\/wp\/v2\/media\/503"}],"wp:attachment":[{"href":"https:\/\/www.yanwenkai.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=451"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.yanwenkai.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=451"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.yanwenkai.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=451"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}