From mboxrd@z Thu Jan 1 00:00:00 1970 From: Archit Taneja Subject: Re: [PATCH v7 2/2] drm/bridge: Add I2C based driver for ps8640 bridge Date: Wed, 13 Jan 2016 12:41:10 +0530 Message-ID: <5695F88E.9040808@codeaurora.org> References: <1452651494-16806-1-git-send-email-jitao.shi@mediatek.com> <1452651494-16806-2-git-send-email-jitao.shi@mediatek.com> Mime-Version: 1.0 Content-Type: text/plain; charset="utf-8"; Format="flowed" Content-Transfer-Encoding: base64 Return-path: In-Reply-To: <1452651494-16806-2-git-send-email-jitao.shi@mediatek.com> List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" To: Jitao Shi Cc: Mark Rutland , stonea168@163.com, dri-devel@lists.freedesktop.org, Ajay Kumar , Vincent Palatin , cawa.cheng@mediatek.com, Russell King , Thierry Reding , devicetree@vger.kernel.org, Pawel Moll , Ian Campbell , Rob Herring , linux-mediatek@lists.infradead.org, yingjoe.chen@mediatek.com, Matthias Brugger , eddie.huang@mediatek.com, linux-arm-kernel@lists.infradead.org, Rahul Sharma , srv_heupstream@mediatek.com, linux-kernel@vger.kernel.org, Sascha Hauer , Kumar Gala , Andy Yan List-Id: linux-mediatek@lists.infradead.org SGkgSml0YW8sCgpPbiAwMS8xMy8yMDE2IDA3OjQ4IEFNLCBKaXRhbyBTaGkgd3JvdGU6Cj4gVGhp cyBwYXRjaCBhZGRzIGRybV9icmlkZ2UgZHJpdmVyIGZvciBwYXJhZGUgRFNJIHRvIGVEUCBicmlk Z2UgY2hpcC4KPgo+IFNpZ25lZC1vZmYtYnk6IEppdGFvIFNoaSA8aml0YW8uc2hpQG1lZGlhdGVr LmNvbT4KPiAtLS0KPiBDaGFuZ2VzIHNpbmNlIHY2Ogo+ICAgLSBBZGQgcHM4NjQwIGZpcm13YXJl IHVwZGF0ZSBmdW5jdGlvbgo+ICAgLSBDaGFuZ2UgaTJjIHRvIGkyY190cmFuc2ZlciBmcm9tIGky Y19tYXN0ZXJfcmVjdi9pMmNfbWFzdGVyX3NlbmQKPiAgIC0gQWRkIGNvbW1lbnRzIGZvciBlYWNo IHBhZ2UgY2xpZW50CgpJJ2QgY29tbWVudGVkIG9uIHRoZSB2NiBwYXRjaCBzZXQgaGVyZToKCmh0 dHBzOi8vbGttbC5vcmcvbGttbC8yMDE1LzEyLzE1LzYyCgpJIHdhbnRlZCB0byBrbm93IGhvdyB0 aGUgYnJpZGdlIGRyaXZlciB3YXMgdHJhbnNmZXJyaW5nIHBhcmFtZXRlcnMKdG8gdGhlIGRzaSBo b3N0LiBJcyB0aGF0IHNvbWV0aGluZyB0aGF0IHlvdSBhcmUgaGFyZGNvZGluZyBvbiBib3RoCnRo ZSBkc2kgaG9zdCBhbmQgcHM4NjQwIGRyaXZlcnMgZm9yIG5vdz8KCk15IGludGVudGlvbiBvZiBh c2tpbmcgdGhpcyBpcyB0byBrbm93IGlmIHRoZXJlIGFyZSBvdGhlciB1c2Vycwp0aGF0IGNhbiB0 YWtlIGFkdmFudGFnZSBvZiB0aGlzIHBhdGNoIHNldDoKCmh0dHBzOi8vbGttbC5vcmcvbGttbC8y MDE1LzEyLzEwLzI4MwoKVGhhbmtzLApBcmNoaXQKCj4gLS0tCj4gICBkcml2ZXJzL2dwdS9kcm0v YnJpZGdlL0tjb25maWcgICAgICAgICB8ICAgMTAgKwo+ICAgZHJpdmVycy9ncHUvZHJtL2JyaWRn ZS9NYWtlZmlsZSAgICAgICAgfCAgICAxICsKPiAgIGRyaXZlcnMvZ3B1L2RybS9icmlkZ2UvcGFy YWRlLXBzODY0MC5jIHwgMTEwNiArKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKwo+ICAg MyBmaWxlcyBjaGFuZ2VkLCAxMTE3IGluc2VydGlvbnMoKykKPiAgIGNyZWF0ZSBtb2RlIDEwMDY0 NCBkcml2ZXJzL2dwdS9kcm0vYnJpZGdlL3BhcmFkZS1wczg2NDAuYwo+Cj4gZGlmZiAtLWdpdCBh L2RyaXZlcnMvZ3B1L2RybS9icmlkZ2UvS2NvbmZpZyBiL2RyaXZlcnMvZ3B1L2RybS9icmlkZ2Uv S2NvbmZpZwo+IGluZGV4IDZkZGRkMzkuLmRjZmRiYzkgMTAwNjQ0Cj4gLS0tIGEvZHJpdmVycy9n cHUvZHJtL2JyaWRnZS9LY29uZmlnCj4gKysrIGIvZHJpdmVycy9ncHUvZHJtL2JyaWRnZS9LY29u ZmlnCj4gQEAgLTQxLDQgKzQxLDE0IEBAIGNvbmZpZyBEUk1fUEFSQURFX1BTODYyMgo+ICAgCS0t LWhlbHAtLS0KPiAgIAkgIFBhcmFkZSBlRFAtTFZEUyBicmlkZ2UgY2hpcCBkcml2ZXIuCj4KPiAr Y29uZmlnIERSTV9QQVJBREVfUFM4NjQwCj4gKwl0cmlzdGF0ZSAiUGFyYWRlIFBTODY0MCBNSVBJ IERTSSB0byBlRFAgQ29udmVydGVyIgo+ICsJZGVwZW5kcyBvbiBPRgo+ICsJc2VsZWN0IERSTV9L TVNfSEVMUEVSCj4gKwlzZWxlY3QgRFJNX1BBTkVMCj4gKwktLS1oZWxwLS0tCj4gKwkgIENob29z ZSB0aGlzIG9wdGlvbiBpZiB5b3UgaGF2ZSBQUzg2NDAgZm9yIGRpc3BsYXkKPiArCSAgVGhlIFBT ODY0MCBpcyBhIGhpZ2gtcGVyZm9ybWFuY2UgYW5kIGxvdy1wb3dlcgo+ICsJICBNSVBJIERTSSB0 byBlRFAgY29udmVydGVyCj4gKwo+ICAgZW5kbWVudQo+IGRpZmYgLS1naXQgYS9kcml2ZXJzL2dw dS9kcm0vYnJpZGdlL01ha2VmaWxlIGIvZHJpdmVycy9ncHUvZHJtL2JyaWRnZS9NYWtlZmlsZQo+ IGluZGV4IGQ0ZTI4YmUuLjI3MmUzYzAxIDEwMDY0NAo+IC0tLSBhL2RyaXZlcnMvZ3B1L2RybS9i cmlkZ2UvTWFrZWZpbGUKPiArKysgYi9kcml2ZXJzL2dwdS9kcm0vYnJpZGdlL01ha2VmaWxlCj4g QEAgLTQsMyArNCw0IEBAIG9iai0kKENPTkZJR19EUk1fRFdfSERNSSkgKz0gZHdfaGRtaS5vCj4g ICBvYmotJChDT05GSUdfRFJNX0RXX0hETUlfQUhCX0FVRElPKSArPSBkd19oZG1pLWFoYi1hdWRp by5vCj4gICBvYmotJChDT05GSUdfRFJNX05YUF9QVE4zNDYwKSArPSBueHAtcHRuMzQ2MC5vCj4g ICBvYmotJChDT05GSUdfRFJNX1BBUkFERV9QUzg2MjIpICs9IHBhcmFkZS1wczg2MjIubwo+ICtv YmotJChDT05GSUdfRFJNX1BBUkFERV9QUzg2NDApICs9IHBhcmFkZS1wczg2NDAubwo+IGRpZmYg LS1naXQgYS9kcml2ZXJzL2dwdS9kcm0vYnJpZGdlL3BhcmFkZS1wczg2NDAuYyBiL2RyaXZlcnMv Z3B1L2RybS9icmlkZ2UvcGFyYWRlLXBzODY0MC5jCj4gbmV3IGZpbGUgbW9kZSAxMDA2NDQKPiBp bmRleCAwMDAwMDAwLi5hMThkOGU5Cj4gLS0tIC9kZXYvbnVsbAo+ICsrKyBiL2RyaXZlcnMvZ3B1 L2RybS9icmlkZ2UvcGFyYWRlLXBzODY0MC5jCj4gQEAgLTAsMCArMSwxMTA2IEBACj4gKy8qCj4g KyAqIENvcHlyaWdodCAoYykgMjAxNCBNZWRpYVRlayBJbmMuCj4gKyAqCj4gKyAqIFRoaXMgcHJv Z3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9k aWZ5Cj4gKyAqIGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExp Y2Vuc2UgdmVyc2lvbiAyIGFzCj4gKyAqIHB1Ymxpc2hlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZSBG b3VuZGF0aW9uLgo+ICsgKgo+ICsgKiBUaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4gdGhl IGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwKPiArICogYnV0IFdJVEhPVVQgQU5ZIFdBUlJB TlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2YKPiArICogTUVSQ0hBTlRB QklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZQo+ICsg KiBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLgo+ICsgKi8KPiAr Cj4gKyNpbmNsdWRlIDxsaW51eC9kZWxheS5oPgo+ICsjaW5jbHVkZSA8bGludXgvZXJyLmg+Cj4g KyNpbmNsdWRlIDxsaW51eC9maXJtd2FyZS5oPgo+ICsjaW5jbHVkZSA8bGludXgvZ3Bpby5oPgo+ ICsjaW5jbHVkZSA8bGludXgvZ3Bpby9jb25zdW1lci5oPgo+ICsjaW5jbHVkZSA8bGludXgvaTJj Lmg+Cj4gKyNpbmNsdWRlIDxsaW51eC9tb2R1bGUuaD4KPiArI2luY2x1ZGUgPGxpbnV4L29mLmg+ Cj4gKyNpbmNsdWRlIDxsaW51eC9vZl9ncGlvLmg+Cj4gKyNpbmNsdWRlIDxsaW51eC9vZl9ncmFw aC5oPgo+ICsjaW5jbHVkZSA8bGludXgvcmVndWxhdG9yL2NvbnN1bWVyLmg+Cj4gKyNpbmNsdWRl IDxhc20vdW5hbGlnbmVkLmg+Cj4gKyNpbmNsdWRlIDxkcm0vZHJtX3BhbmVsLmg+Cj4gKwo+ICsj aW5jbHVkZSA8ZHJtUC5oPgo+ICsjaW5jbHVkZSA8ZHJtX2NydGNfaGVscGVyLmg+Cj4gKyNpbmNs dWRlIDxkcm1fY3J0Yy5oPgo+ICsjaW5jbHVkZSA8ZHJtX2F0b21pY19oZWxwZXIuaD4KPiArCj4g KyNkZWZpbmUgUEFHRTJfU1BJX0NGRzMJCTB4ODIKPiArI2RlZmluZSBJMkNfVE9fU1BJX1JFU0VU CTB4MjAKPiArI2RlZmluZSBQQUdFMl9ST01BRERfQllURTEJMHg4ZQo+ICsjZGVmaW5lIFBBR0Uy X1JPTUFERF9CWVRFMgkweDhmCj4gKyNkZWZpbmUgUEFHRTJfU1dTUElfV0RBVEEJMHg5MAo+ICsj ZGVmaW5lIFBBR0UyX1NXU1BJX1JEQVRBCTB4OTEKPiArI2RlZmluZSBQQUdFMl9TV1NQSV9MRU4J CTB4OTIKPiArI2RlZmluZSBQQUdFMl9TV1NQSV9DVEwJCTB4OTMKPiArI2RlZmluZSBUUklHR0VS X05PX1JFQURCQUNLCTB4MDUKPiArI2RlZmluZSBUUklHR0VSX1JFQURCQUNLCTB4MDEKPiArI2Rl ZmluZSBQQUdFMl9TUElfU1RBVFVTCTB4OWUKPiArI2RlZmluZSBQQUdFMl9HUElPX0wJCTB4YTYK PiArI2RlZmluZSBQQUdFMl9HUElPX0gJCTB4YTcKPiArI2RlZmluZSBQU19HUElPOQkJQklUKDEp Cj4gKyNkZWZpbmUgUEFHRTJfSVJPTV9DVFJMCQkweGIwCj4gKyNkZWZpbmUgSVJPTV9FTkFCTEUJ CTB4YzAKPiArI2RlZmluZSBJUk9NX0RJU0FCTEUJCTB4ODAKPiArI2RlZmluZSBQQUdFMl9TV19S RVNUCQkweGJjCj4gKyNkZWZpbmUgUEFHRTJfRU5DVExTUElfV1IJMHhkYQo+ICsjZGVmaW5lIFBB R0UyX0kyQ19CWVBBU1MJMHhlYQo+ICsjZGVmaW5lIEkyQ19CWVBBU1NfRU4JCTB4ZDAKPiArCj4g KyNkZWZpbmUgUEFHRTNfU0VUX0FERAkJMHhmZQo+ICsjZGVmaW5lIFBBR0UzX1NFVF9WQUwJCTB4 ZmYKPiArI2RlZmluZSBWRE9fQ1RMX0FERAkJMHgxMwo+ICsjZGVmaW5lIFZET19ESVMJCQkweDE4 Cj4gKyNkZWZpbmUgVkRPX0VOCQkJMHgxYwo+ICsKPiArI2RlZmluZSBQQUdFNF9SRVZfTAkJMHhm MAo+ICsjZGVmaW5lIFBBR0U0X1JFVl9ICQkweGYxCj4gKyNkZWZpbmUgUEFHRTRfQ0hJUF9MCQkw eGYyCj4gKyNkZWZpbmUgUEFHRTRfQ0hJUF9ICQkweGYzCj4gKwo+ICsvKiBGaXJtd2FyZSAqLwo+ ICsjZGVmaW5lIFNQSV9NQVhfUkVUUllfQ05UCTgKPiArI2RlZmluZSBQU19GV19OQU1FCQkicHM4 NjR4X2Z3LmJpbiIKPiArCj4gKyNkZWZpbmUgRldfQ0hJUF9JRF9PRkZTRVQJCTAKPiArI2RlZmlu ZSBGV19WRVJTSU9OX09GRlNFVAkJMgo+ICsKPiArI2RlZmluZSBicmlkZ2VfdG9fcHM4NjQwKGUp CWNvbnRhaW5lcl9vZihlLCBzdHJ1Y3QgcHM4NjQwLCBicmlkZ2UpCj4gKyNkZWZpbmUgY29ubmVj dG9yX3RvX3BzODY0MChlKQljb250YWluZXJfb2YoZSwgc3RydWN0IHBzODY0MCwgY29ubmVjdG9y KQo+ICsKPiArc3RydWN0IHBzODY0MF9pbmZvIHsKPiArCXU4IGZhbWlseV9pZDsKPiArCXU4IHZh cmlhbnRfaWQ7Cj4gKwl1MTYgdmVyc2lvbjsKPiArfTsKPiArCj4gK3N0cnVjdCBwczg2NDAgewo+ ICsJc3RydWN0IGRybV9jb25uZWN0b3IgY29ubmVjdG9yOwo+ICsJc3RydWN0IGRybV9icmlkZ2Ug YnJpZGdlOwo+ICsJc3RydWN0IGkyY19jbGllbnQgKnBhZ2VbOF07Cj4gKwlzdHJ1Y3QgcHM4NjQw X2RyaXZlcl9kYXRhICpkcml2ZXJfZGF0YTsKPiArCXN0cnVjdCByZWd1bGF0b3IgKnB3cl8xdjJf c3VwcGx5Owo+ICsJc3RydWN0IHJlZ3VsYXRvciAqcHdyXzN2M19zdXBwbHk7Cj4gKwlzdHJ1Y3Qg ZHJtX3BhbmVsICpwYW5lbDsKPiArCXN0cnVjdCBncGlvX2Rlc2MgKmdwaW9fcnN0X247Cj4gKwlz dHJ1Y3QgZ3Bpb19kZXNjICpncGlvX3NscF9uOwo+ICsJc3RydWN0IGdwaW9fZGVzYyAqZ3Bpb19t b2RlX3NlbF9uOwo+ICsJYm9vbCBlbmFibGVkOwo+ICsKPiArCS8qIGZpcm13YXJlIGZpbGUgbmFt ZSAqLwo+ICsJYm9vbCBpbl9md191cGRhdGU7Cj4gKwljaGFyICpmd19maWxlOwo+ICsJc3RydWN0 IHBzODY0MF9pbmZvIGluZm87Cj4gK307Cj4gKwo+ICtzdGF0aWMgY29uc3QgdTggZW5jX2N0cmxf Y29kZVs2XSA9IHsweGFhLCAweDU1LCAweDUwLCAweDQxLCAweDUyLCAweDQ0fTsKPiArCj4gK3N0 YXRpYyBpbnQgcHM4NjQwX3JlZ3Ioc3RydWN0IGkyY19jbGllbnQgKmNsaWVudCwgdTggcmVnLCB1 OCAqZGF0YSwKPiArCQkgICAgICAgdTE2IGRhdGFfbGVuKQo+ICt7Cj4gKwlpbnQgcmV0Owo+ICsK PiArCXN0cnVjdCBpMmNfbXNnIG1zZ3NbXSA9IHsKPiArCQl7Cj4gKwkJIC5hZGRyID0gY2xpZW50 LT5hZGRyLAo+ICsJCSAuZmxhZ3MgPSAwLAo+ICsJCSAubGVuID0gMSwKPiArCQkgLmJ1ZiA9ICZy ZWcsCj4gKwkJIH0sCj4gKwkJewo+ICsJCSAuYWRkciA9IGNsaWVudC0+YWRkciwKPiArCQkgLmZs YWdzID0gSTJDX01fUkQsCj4gKwkJIC5sZW4gPSBkYXRhX2xlbiwKPiArCQkgLmJ1ZiA9IGRhdGEs Cj4gKwkJIH0KPiArCX07Cj4gKwo+ICsJcmV0ID0gaTJjX3RyYW5zZmVyKGNsaWVudC0+YWRhcHRl ciwgbXNncywgMik7Cj4gKwo+ICsJaWYgKHJldCA9PSAyKQo+ICsJCXJldHVybiAwOwo+ICsJaWYg KHJldCA8IDApCj4gKwkJcmV0dXJuIHJldDsKPiArCWVsc2UKPiArCQlyZXR1cm4gLUVJTzsKPiAr fQo+ICsKPiArc3RhdGljIGludCBwczg2NDBfcmVndyhzdHJ1Y3QgaTJjX2NsaWVudCAqY2xpZW50 LCB1OCByZWcsICBjb25zdCB1OCAqZGF0YSwKPiArCQkgICAgICAgdTE2IGRhdGFfbGVuKQo+ICt7 Cj4gKwlpbnQgcmV0LCBpOwo+ICsJc3RydWN0IGkyY19tc2cgbXNnOwo+ICsJdTggKmJ1ZjsKPiAr Cj4gKwlidWYgPSBkZXZtX2t6YWxsb2MoJmNsaWVudC0+ZGV2LCBkYXRhX2xlbiArIDEsIEdGUF9L RVJORUwpOwo+ICsJLyogaTJjIHBhZ2Ugc2l6ZSBpcyAyNTYgYnl0ZXMsIHNvIGxpbWl0IHRoZSBk YXRhX2xlbiAyNTYgKi8KPiArCWlmIChkYXRhX2xlbiA+IDI1Nikgewo+ICsJCWRldl9lcnIoJmNs aWVudC0+ZGV2LCAiZGF0YV9sZW4gbXVzdCB1bmRlciAyNTY6IGxlbiA9ICVkXG4iLAo+ICsJCQlk YXRhX2xlbik7Cj4gKwkJcmV0dXJuIC1FSU87Cj4gKwl9Cj4gKwlmb3IgKGkgPSAwOyBpIDwgZGF0 YV9sZW4gKyAxOyBpKyspCj4gKwkJYnVmW2ldID0gKGkgPT0gMCkgPyByZWcgOiBkYXRhW2kgLSAx XTsKPiArCj4gKwltc2cuYWRkciA9IGNsaWVudC0+YWRkcjsKPiArCW1zZy5mbGFncyA9IDA7Cj4g Kwltc2cubGVuID0gZGF0YV9sZW4gKyAxOwo+ICsJbXNnLmJ1ZiA9IGJ1ZjsKPiArCj4gKwlyZXQg PSBpMmNfdHJhbnNmZXIoY2xpZW50LT5hZGFwdGVyLCAmbXNnLCAxKTsKPiArCWlmIChyZXQgPT0g MSkKPiArCQlyZXR1cm4gMDsKPiArCWlmIChyZXQgPCAwKQo+ICsJCXJldHVybiByZXQ7Cj4gKwll bHNlCj4gKwkJcmV0dXJuIC1FSU87Cj4gK30KPiArCj4gK3N0YXRpYyBpbnQgcHM4NjQwX3JlZ3df Ynl0ZShzdHJ1Y3QgaTJjX2NsaWVudCAqY2xpZW50LCB1OCByZWcsICB1OCBkYXRhKQo+ICt7Cj4g KwlpbnQgcmV0Owo+ICsJc3RydWN0IGkyY19tc2cgbXNnOwo+ICsJdTggYnVmW10gPSB7cmVnLCBk YXRhfTsKPiArCj4gKwltc2cuYWRkciA9IGNsaWVudC0+YWRkcjsKPiArCW1zZy5mbGFncyA9IDA7 Cj4gKwltc2cubGVuID0gc2l6ZW9mKGJ1Zik7Cj4gKwltc2cuYnVmID0gYnVmOwo+ICsKPiArCXJl dCA9IGkyY190cmFuc2ZlcihjbGllbnQtPmFkYXB0ZXIsICZtc2csIDEpOwo+ICsJaWYgKHJldCA9 PSAxKQo+ICsJCXJldHVybiAwOwo+ICsJaWYgKHJldCA8IDApCj4gKwkJcmV0dXJuIHJldDsKPiAr CWVsc2UKPiArCQlyZXR1cm4gLUVJTzsKPiArfQo+ICsKPiArc3RhdGljIGludCBwczg2NDBfY2hl Y2tfdmFsaWRfaWQoc3RydWN0IHBzODY0MCAqcHNfYnJpZGdlKQo+ICt7Cj4gKwlzdHJ1Y3QgaTJj X2NsaWVudCAqY2xpZW50ID0gcHNfYnJpZGdlLT5wYWdlWzRdOwo+ICsJdTggY2hpcF9pZFs0XTsK PiArCj4gKwlwczg2NDBfcmVncihjbGllbnQsIFBBR0U0X1JFVl9MLCBjaGlwX2lkLCA0KTsKPiAr Cj4gKwlpZiAoKGNoaXBfaWRbMF0gPT0gMHgwMCkgJiYgKGNoaXBfaWRbMV0gPT0gMHgwYSkgJiYK PiArCSAgICAoY2hpcF9pZFsyXSA9PSAweDAwKSAmJiAoY2hpcF9pZFszXSA9PSAweDMwKSkKPiAr CQlyZXR1cm4gMDsKPiArCj4gKwlyZXR1cm4gLUVOT0RFVjsKPiArfQo+ICsKPiArc3RhdGljIHZv aWQgcHM4NjQwX3Nob3dfbWN1X2Z3X3ZlcnNpb24oc3RydWN0IHBzODY0MCAqcHNfYnJpZGdlKQo+ ICt7Cj4gKwlzdHJ1Y3QgaTJjX2NsaWVudCAqY2xpZW50ID0gcHNfYnJpZGdlLT5wYWdlWzVdOwo+ ICsJdTggZndfdmVyWzJdOwo+ICsKPiArCXBzODY0MF9yZWdyKGNsaWVudCwgMHg0LCBmd192ZXIs IDIpOwo+ICsJcHNfYnJpZGdlLT5pbmZvLnZlcnNpb24gPSAoZndfdmVyWzBdIDw8IDgpIHwgZndf dmVyWzFdOwo+ICsKPiArCURSTV9JTkZPX09OQ0UoInBzODY0MCByb20gZncgdmVyc2lvbiAlZC4l ZFxuIiwgZndfdmVyWzBdLCBmd192ZXJbMV0pOwo+ICt9Cj4gKwo+ICtzdGF0aWMgaW50IHBzODY0 MF9iZGdfZW5hYmxlKHN0cnVjdCBwczg2NDAgKnBzX2JyaWRnZSkKPiArewo+ICsJc3RydWN0IGky Y19jbGllbnQgKmNsaWVudCA9IHBzX2JyaWRnZS0+cGFnZVszXTsKPiArCWludCByZXQ7Cj4gKwl1 OCB2ZG9fY3RybFsyXSA9IHtWRE9fQ1RMX0FERCwgVkRPX0VOfTsKPiArCj4gKwlyZXQgPSBwczg2 NDBfY2hlY2tfdmFsaWRfaWQocHNfYnJpZGdlKTsKPiArCWlmIChyZXQpIHsKPiArCQlEUk1fRVJS T1IoInBzODY0MCBub3QgdmFsaWQ6ICVkXG4iLCByZXQpOwo+ICsJCXJldHVybiByZXQ7Cj4gKwl9 Cj4gKwo+ICsJcmV0ID0gcHM4NjQwX3JlZ3coY2xpZW50LCBQQUdFM19TRVRfQURELCB2ZG9fY3Ry bCwgMik7Cj4gKwlyZXR1cm4gcmV0Owo+ICt9Cj4gKwo+ICtzdGF0aWMgYm9vbCBwczg2NDBfaW5f Ym9vdGxvYWRlcihzdHJ1Y3QgcHM4NjQwICpwc19icmlkZ2UpCj4gK3sKPiArCXJldHVybiBwc19i cmlkZ2UtPmluX2Z3X3VwZGF0ZTsKPiArfQo+ICsKPiArc3RhdGljIHZvaWQgcHM4NjQwX3ByZXBh cmUoc3RydWN0IHBzODY0MCAqcHNfYnJpZGdlKQo+ICt7Cj4gKwlzdHJ1Y3QgaTJjX2NsaWVudCAq Y2xpZW50ID0gcHNfYnJpZGdlLT5wYWdlWzJdOwo+ICsJaW50IGVyciwgcmV0cnlfY250ID0gMDsK PiArCXU4IHNldF92ZG9fZG9uZTsKPiArCj4gKwlpZiAocHM4NjQwX2luX2Jvb3Rsb2FkZXIocHNf YnJpZGdlKSkKPiArCQlyZXR1cm47Cj4gKwo+ICsJaWYgKHBzX2JyaWRnZS0+ZW5hYmxlZCkKPiAr CQlyZXR1cm47Cj4gKwo+ICsJZXJyID0gZHJtX3BhbmVsX3ByZXBhcmUocHNfYnJpZGdlLT5wYW5l bCk7Cj4gKwlpZiAoZXJyIDwgMCkgewo+ICsJCURSTV9FUlJPUigiZmFpbGVkIHRvIHByZXBhcmUg cGFuZWw6ICVkXG4iLCBlcnIpOwo+ICsJCXJldHVybjsKPiArCX0KPiArCj4gKwkvKiBkZWxheSBm b3IgcG93ZXIgc3RhYmxlICovCj4gKwl1c2xlZXBfcmFuZ2UoNTAwLCA3MDApOwo+ICsKPiArCWVy ciA9IHJlZ3VsYXRvcl9lbmFibGUocHNfYnJpZGdlLT5wd3JfMXYyX3N1cHBseSk7Cj4gKwlpZiAo ZXJyIDwgMCkgewo+ICsJCURSTV9FUlJPUigiZmFpbGVkIHRvIGVuYWJsZSB2ZGQxMi1zdXBwbHk6 ICVkXG4iLCBlcnIpOwo+ICsJCWdvdG8gZXJyX3BhbmVsX3VucHJlcGFyZTsKPiArCX0KPiArCj4g KwllcnIgPSByZWd1bGF0b3JfZW5hYmxlKHBzX2JyaWRnZS0+cHdyXzN2M19zdXBwbHkpOwo+ICsJ aWYgKGVyciA8IDApIHsKPiArCQlEUk1fRVJST1IoImZhaWxlZCB0byBlbmFibGUgdmRkMzMtc3Vw cGx5OiAlZFxuIiwgZXJyKTsKPiArCQlnb3RvIGVycl9yZWd1bGF0b3JfMXYyX2Rpc2FibGU7Cj4g Kwl9Cj4gKwo+ICsJZ3Bpb2Rfc2V0X3ZhbHVlKHBzX2JyaWRnZS0+Z3Bpb19zbHBfbiwgMSk7Cj4g KwlncGlvZF9zZXRfdmFsdWUocHNfYnJpZGdlLT5ncGlvX3JzdF9uLCAwKTsKPiArCXVzbGVlcF9y YW5nZSg1MDAsIDcwMCk7Cj4gKwlncGlvZF9zZXRfdmFsdWUocHNfYnJpZGdlLT5ncGlvX3JzdF9u LCAxKTsKPiArCj4gKwkvKiB3YWl0IGZvciB0aGUgcHM4NjQwIGVtYmVkIG1jdSByZWFkeQo+ICsJ ICAqIGZpcnN0IHdhaXQgMjAwbXMgYW5kIHRoZW4gY2hlY2sgdGhlIG1jdSByZWFkeSBmbGFnIGV2 ZXJ5IDIwbXMKPiArCSovCj4gKwltc2xlZXAoMjAwKTsKPiArCWRvIHsKPiArCQllcnIgPSBwczg2 NDBfcmVncihjbGllbnQsIFBBR0UyX0dQSU9fSCwgJnNldF92ZG9fZG9uZSwgMSk7Cj4gKwkJaWYg KGVyciA8IDApIHsKPiArCQkJRFJNX0VSUk9SKCJmYWlsZWQgcmVhZCBQQUdFMl9HUElPX0g6ICVk XG4iLCBlcnIpOwo+ICsJCQlnb3RvIGVycl9yZWd1bGF0b3JfMXYyX2Rpc2FibGU7Cj4gKwkJfQo+ ICsJCW1zbGVlcCgyMCk7Cj4gKwl9IHdoaWxlICgocmV0cnlfY250KysgPCAxMCkgJiYgKChzZXRf dmRvX2RvbmUgJiBQU19HUElPOSkgIT0gUFNfR1BJTzkpKTsKPiArCj4gKwlwczg2NDBfc2hvd19t Y3VfZndfdmVyc2lvbihwc19icmlkZ2UpOwo+ICsJcHM4NjQwX3JlZ3dfYnl0ZShjbGllbnQsIFBB R0UyX0kyQ19CWVBBU1MsIEkyQ19CWVBBU1NfRU4pOwo+ICsJcHNfYnJpZGdlLT5lbmFibGVkID0g dHJ1ZTsKPiArCj4gKwlyZXR1cm47Cj4gKwo+ICtlcnJfcmVndWxhdG9yXzF2Ml9kaXNhYmxlOgo+ ICsJcmVndWxhdG9yX2Rpc2FibGUocHNfYnJpZGdlLT5wd3JfMXYyX3N1cHBseSk7Cj4gK2Vycl9w YW5lbF91bnByZXBhcmU6Cj4gKwlkcm1fcGFuZWxfdW5wcmVwYXJlKHBzX2JyaWRnZS0+cGFuZWwp Owo+ICt9Cj4gKwo+ICtzdGF0aWMgdm9pZCBwczg2NDBfcHJlX2VuYWJsZShzdHJ1Y3QgZHJtX2Jy aWRnZSAqYnJpZGdlKQo+ICt7Cj4gKwlzdHJ1Y3QgcHM4NjQwICpwc19icmlkZ2UgPSBicmlkZ2Vf dG9fcHM4NjQwKGJyaWRnZSk7Cj4gKwo+ICsJcHM4NjQwX3ByZXBhcmUocHNfYnJpZGdlKTsKPiAr fQo+ICsKPiArc3RhdGljIHZvaWQgcHM4NjQwX2VuYWJsZShzdHJ1Y3QgZHJtX2JyaWRnZSAqYnJp ZGdlKQo+ICt7Cj4gKwlzdHJ1Y3QgcHM4NjQwICpwc19icmlkZ2UgPSBicmlkZ2VfdG9fcHM4NjQw KGJyaWRnZSk7Cj4gKwlpbnQgZXJyOwo+ICsKPiArCWVyciA9IHBzODY0MF9iZGdfZW5hYmxlKHBz X2JyaWRnZSk7Cj4gKwlpZiAoZXJyKQo+ICsJCURSTV9FUlJPUigiZmFpbGVkIHRvIGVuYWJsZSB1 bm11dGV2aWRlbzogJWRcbiIsIGVycik7Cj4gKwo+ICsJZXJyID0gZHJtX3BhbmVsX2VuYWJsZShw c19icmlkZ2UtPnBhbmVsKTsKPiArCWlmIChlcnIgPCAwKQo+ICsJCURSTV9FUlJPUigiZmFpbGVk IHRvIGVuYWJsZSBwYW5lbDogJWRcbiIsIGVycik7Cj4gK30KPiArCj4gK3N0YXRpYyB2b2lkIHBz ODY0MF9kaXNhYmxlKHN0cnVjdCBkcm1fYnJpZGdlICpicmlkZ2UpCj4gK3sKPiArCXN0cnVjdCBw czg2NDAgKnBzX2JyaWRnZSA9IGJyaWRnZV90b19wczg2NDAoYnJpZGdlKTsKPiArCWludCBlcnI7 Cj4gKwo+ICsJaWYgKHBzODY0MF9pbl9ib290bG9hZGVyKHBzX2JyaWRnZSkpCj4gKwkJcmV0dXJu Owo+ICsKPiArCWlmICghcHNfYnJpZGdlLT5lbmFibGVkKQo+ICsJCXJldHVybjsKPiArCj4gKwlw c19icmlkZ2UtPmVuYWJsZWQgPSBmYWxzZTsKPiArCj4gKwllcnIgPSBkcm1fcGFuZWxfZGlzYWJs ZShwc19icmlkZ2UtPnBhbmVsKTsKPiArCWlmIChlcnIgPCAwKQo+ICsJCURSTV9FUlJPUigiZmFp bGVkIHRvIGRpc2FibGUgcGFuZWw6ICVkXG4iLCBlcnIpOwo+ICsKPiArCWdwaW9kX3NldF92YWx1 ZShwc19icmlkZ2UtPmdwaW9fcnN0X24sIDApOwo+ICsJZ3Bpb2Rfc2V0X3ZhbHVlKHBzX2JyaWRn ZS0+Z3Bpb19zbHBfbiwgMCk7Cj4gKwlyZWd1bGF0b3JfZGlzYWJsZShwc19icmlkZ2UtPnB3cl8z djNfc3VwcGx5KTsKPiArCXJlZ3VsYXRvcl9kaXNhYmxlKHBzX2JyaWRnZS0+cHdyXzF2Ml9zdXBw bHkpOwo+ICt9Cj4gKwo+ICtzdGF0aWMgdm9pZCBwczg2NDBfcG9zdF9kaXNhYmxlKHN0cnVjdCBk cm1fYnJpZGdlICpicmlkZ2UpCj4gK3sKPiArCXN0cnVjdCBwczg2NDAgKnBzX2JyaWRnZSA9IGJy aWRnZV90b19wczg2NDAoYnJpZGdlKTsKPiArCWludCBlcnI7Cj4gKwo+ICsJZXJyID0gZHJtX3Bh bmVsX3VucHJlcGFyZShwc19icmlkZ2UtPnBhbmVsKTsKPiArCWlmIChlcnIpCj4gKwkJRFJNX0VS Uk9SKCJmYWlsZWQgdG8gdW5wcmVwYXJlIHBhbmVsOiAlZFxuIiwgZXJyKTsKPiArfQo+ICsKPiAr c3RhdGljIGludCBwczg2NDBfZ2V0X21vZGVzKHN0cnVjdCBkcm1fY29ubmVjdG9yICpjb25uZWN0 b3IpCj4gK3sKPiArCXN0cnVjdCBwczg2NDAgKnBzX2JyaWRnZSA9IGNvbm5lY3Rvcl90b19wczg2 NDAoY29ubmVjdG9yKTsKPiArCj4gKwlwczg2NDBfcHJlcGFyZShwc19icmlkZ2UpOwo+ICsKPiAr CXJldHVybiBkcm1fcGFuZWxfZ2V0X21vZGVzKHBzX2JyaWRnZS0+cGFuZWwpOwo+ICt9Cj4gKwo+ ICtzdGF0aWMgc3RydWN0IGRybV9lbmNvZGVyICpwczg2NDBfYmVzdF9lbmNvZGVyKHN0cnVjdCBk cm1fY29ubmVjdG9yICpjb25uZWN0b3IpCj4gK3sKPiArCXN0cnVjdCBwczg2NDAgKnBzX2JyaWRn ZTsKPiArCj4gKwlwc19icmlkZ2UgPSBjb25uZWN0b3JfdG9fcHM4NjQwKGNvbm5lY3Rvcik7Cj4g KwlyZXR1cm4gcHNfYnJpZGdlLT5icmlkZ2UuZW5jb2RlcjsKPiArfQo+ICsKPiArc3RhdGljIGNv bnN0IHN0cnVjdCBkcm1fY29ubmVjdG9yX2hlbHBlcl9mdW5jcyBwczg2NDBfY29ubmVjdG9yX2hl bHBlcl9mdW5jcyA9IHsKPiArCS5nZXRfbW9kZXMgPSBwczg2NDBfZ2V0X21vZGVzLAo+ICsJLmJl c3RfZW5jb2RlciA9IHBzODY0MF9iZXN0X2VuY29kZXIsCj4gK307Cj4gKwo+ICtzdGF0aWMgZW51 bSBkcm1fY29ubmVjdG9yX3N0YXR1cyBwczg2NDBfZGV0ZWN0KHN0cnVjdCBkcm1fY29ubmVjdG9y ICpjb25uZWN0b3IsCj4gKwkJCQkJICAgICAgIGJvb2wgZm9yY2UpCj4gK3sKPiArCXJldHVybiBj b25uZWN0b3Jfc3RhdHVzX2Nvbm5lY3RlZDsKPiArfQo+ICsKPiArc3RhdGljIHZvaWQgcHM4NjQw X2Nvbm5lY3Rvcl9kZXN0cm95KHN0cnVjdCBkcm1fY29ubmVjdG9yICpjb25uZWN0b3IpCj4gK3sK PiArCWRybV9jb25uZWN0b3JfdW5yZWdpc3Rlcihjb25uZWN0b3IpOwo+ICsJZHJtX2Nvbm5lY3Rv cl9jbGVhbnVwKGNvbm5lY3Rvcik7Cj4gK30KPiArCj4gK3N0YXRpYyBjb25zdCBzdHJ1Y3QgZHJt X2Nvbm5lY3Rvcl9mdW5jcyBwczg2NDBfY29ubmVjdG9yX2Z1bmNzID0gewo+ICsJLmRwbXMgPSBk cm1fYXRvbWljX2hlbHBlcl9jb25uZWN0b3JfZHBtcywKPiArCS5maWxsX21vZGVzID0gZHJtX2hl bHBlcl9wcm9iZV9zaW5nbGVfY29ubmVjdG9yX21vZGVzLAo+ICsJLmRldGVjdCA9IHBzODY0MF9k ZXRlY3QsCj4gKwkuZGVzdHJveSA9IHBzODY0MF9jb25uZWN0b3JfZGVzdHJveSwKPiArCS5yZXNl dCA9IGRybV9hdG9taWNfaGVscGVyX2Nvbm5lY3Rvcl9yZXNldCwKPiArCS5hdG9taWNfZHVwbGlj YXRlX3N0YXRlID0gZHJtX2F0b21pY19oZWxwZXJfY29ubmVjdG9yX2R1cGxpY2F0ZV9zdGF0ZSwK PiArCS5hdG9taWNfZGVzdHJveV9zdGF0ZSA9IGRybV9hdG9taWNfaGVscGVyX2Nvbm5lY3Rvcl9k ZXN0cm95X3N0YXRlLAo+ICt9Owo+ICsKPiAraW50IHBzODY0MF9icmlkZ2VfYXR0YWNoKHN0cnVj dCBkcm1fYnJpZGdlICpicmlkZ2UpCj4gK3sKPiArCXN0cnVjdCBwczg2NDAgKnBzX2JyaWRnZSA9 IGJyaWRnZV90b19wczg2NDAoYnJpZGdlKTsKPiArCWludCByZXQ7Cj4gKwo+ICsJaWYgKCFicmlk Z2UtPmVuY29kZXIpIHsKPiArCQlEUk1fRVJST1IoIlBhcmVudCBlbmNvZGVyIG9iamVjdCBub3Qg Zm91bmQiKTsKPiArCQlyZXR1cm4gLUVOT0RFVjsKPiArCX0KPiArCj4gKwlyZXQgPSBkcm1fY29u bmVjdG9yX2luaXQoYnJpZGdlLT5kZXYsICZwc19icmlkZ2UtPmNvbm5lY3RvciwKPiArCQkJCSAm cHM4NjQwX2Nvbm5lY3Rvcl9mdW5jcywKPiArCQkJCSBEUk1fTU9ERV9DT05ORUNUT1JfZURQKTsK PiArCj4gKwlpZiAocmV0KSB7Cj4gKwkJRFJNX0VSUk9SKCJGYWlsZWQgdG8gaW5pdGlhbGl6ZSBj b25uZWN0b3Igd2l0aCBkcm06ICVkXG4iLCByZXQpOwo+ICsJCXJldHVybiByZXQ7Cj4gKwl9Cj4g Kwo+ICsJZHJtX2Nvbm5lY3Rvcl9oZWxwZXJfYWRkKCZwc19icmlkZ2UtPmNvbm5lY3RvciwKPiAr CQkJCSAmcHM4NjQwX2Nvbm5lY3Rvcl9oZWxwZXJfZnVuY3MpOwo+ICsJZHJtX2Nvbm5lY3Rvcl9y ZWdpc3RlcigmcHNfYnJpZGdlLT5jb25uZWN0b3IpOwo+ICsKPiArCXBzX2JyaWRnZS0+Y29ubmVj dG9yLmRwbXMgPSBEUk1fTU9ERV9EUE1TX09OOwo+ICsJZHJtX21vZGVfY29ubmVjdG9yX2F0dGFj aF9lbmNvZGVyKCZwc19icmlkZ2UtPmNvbm5lY3RvciwKPiArCQkJCQkgIGJyaWRnZS0+ZW5jb2Rl cik7Cj4gKwo+ICsJaWYgKHBzX2JyaWRnZS0+cGFuZWwpCj4gKwkJZHJtX3BhbmVsX2F0dGFjaChw c19icmlkZ2UtPnBhbmVsLCAmcHNfYnJpZGdlLT5jb25uZWN0b3IpOwo+ICsKPiArCXJldHVybiBy ZXQ7Cj4gK30KPiArCj4gK3N0YXRpYyBib29sIHBzODY0MF9icmlkZ2VfbW9kZV9maXh1cChzdHJ1 Y3QgZHJtX2JyaWRnZSAqYnJpZGdlLAo+ICsJCQkJICAgICBjb25zdCBzdHJ1Y3QgZHJtX2Rpc3Bs YXlfbW9kZSAqbW9kZSwKPiArCQkJCSAgICAgc3RydWN0IGRybV9kaXNwbGF5X21vZGUgKmFkanVz dGVkX21vZGUpCj4gK3sKPiArCXJldHVybiB0cnVlOwo+ICt9Cj4gKwo+ICtzdGF0aWMgY29uc3Qg c3RydWN0IGRybV9icmlkZ2VfZnVuY3MgcHM4NjQwX2JyaWRnZV9mdW5jcyA9IHsKPiArCS5hdHRh Y2ggPSBwczg2NDBfYnJpZGdlX2F0dGFjaCwKPiArCS5tb2RlX2ZpeHVwID0gcHM4NjQwX2JyaWRn ZV9tb2RlX2ZpeHVwLAo+ICsJLmRpc2FibGUgPSBwczg2NDBfZGlzYWJsZSwKPiArCS5wb3N0X2Rp c2FibGUgPSBwczg2NDBfcG9zdF9kaXNhYmxlLAo+ICsJLnByZV9lbmFibGUgPSBwczg2NDBfcHJl X2VuYWJsZSwKPiArCS5lbmFibGUgPSBwczg2NDBfZW5hYmxlLAo+ICt9Owo+ICsKPiArc3RhdGlj IHNzaXplX3QgcHM4NjQwX2Z3X2ZpbGVfc2hvdyhzdHJ1Y3QgZGV2aWNlICpkZXYsCj4gKwkJCQkg ICBzdHJ1Y3QgZGV2aWNlX2F0dHJpYnV0ZSAqYXR0ciwgY2hhciAqYnVmKQo+ICt7Cj4gKwlzdHJ1 Y3QgcHM4NjQwICpwc19icmlkZ2UgPSBkZXZfZ2V0X2RydmRhdGEoZGV2KTsKPiArCj4gKwlyZXR1 cm4gc2NucHJpbnRmKGJ1ZiwgUEFHRV9TSVpFLCAiJXNcbiIsIHBzX2JyaWRnZS0+ZndfZmlsZSk7 Cj4gK30KPiArCj4gK3N0YXRpYyBpbnQgcHM4NjQwX3VwZGF0ZV9maWxlX25hbWUoc3RydWN0IGRl dmljZSAqZGV2LCBjaGFyICoqZmlsZV9uYW1lLAo+ICsJCQkJICAgY29uc3QgY2hhciAqYnVmLCBz aXplX3QgY291bnQpCj4gK3sKPiArCWNoYXIgKm5ld19maWxlX25hbWU7Cj4gKwo+ICsJLyogU2lt cGxlIHNhbml0eSBjaGVjayAqLwo+ICsJaWYgKGNvdW50ID4gNjQpIHsKPiArCQlkZXZfd2Fybihk ZXYsICJGaWxlIG5hbWUgdG9vIGxvbmdcbiIpOwo+ICsJCXJldHVybiAtRUlOVkFMOwo+ICsJfQo+ ICsKPiArCW5ld19maWxlX25hbWUgPSBkZXZtX2ttYWxsb2MoZGV2LCBjb3VudCArIDEsIEdGUF9L RVJORUwpOwo+ICsJaWYgKCFuZXdfZmlsZV9uYW1lKQo+ICsJCXJldHVybiAtRU5PTUVNOwo+ICsK PiArCW1lbWNweShuZXdfZmlsZV9uYW1lLCBidWYsIGNvdW50ICsgMSk7Cj4gKwo+ICsJLyogRWNo byBpbnRvIHRoZSBzeXNmcyBlbnRyeSBtYXkgYXBwZW5kIG5ld2xpbmUgYXQgdGhlIGVuZCBvZiBi dWYgKi8KPiArCWlmIChuZXdfZmlsZV9uYW1lW2NvdW50IC0gMV0gPT0gJ1xuJykKPiArCQljb3Vu dC0tOwo+ICsKPiArCW5ld19maWxlX25hbWVbY291bnRdID0gJ1wwJzsKPiArCj4gKwlpZiAoKmZp bGVfbmFtZSkKPiArCQlkZXZtX2tmcmVlKGRldiwgKmZpbGVfbmFtZSk7Cj4gKwo+ICsJKmZpbGVf bmFtZSA9IG5ld19maWxlX25hbWU7Cj4gKwo+ICsJcmV0dXJuIDA7Cj4gK30KPiArCj4gK3N0YXRp YyBzc2l6ZV90IHBzODY0MF9md19maWxlX3N0b3JlKHN0cnVjdCBkZXZpY2UgKmRldiwKPiArCQkJ CSAgICBzdHJ1Y3QgZGV2aWNlX2F0dHJpYnV0ZSAqYXR0ciwKPiArCQkJCSAgICBjb25zdCBjaGFy ICpidWYsIHNpemVfdCBjb3VudCkKPiArewo+ICsJc3RydWN0IHBzODY0MCAqcHNfYnJpZGdlID0g ZGV2X2dldF9kcnZkYXRhKGRldik7Cj4gKwlpbnQgcmV0Owo+ICsKPiArCXJldCA9IHBzODY0MF91 cGRhdGVfZmlsZV9uYW1lKGRldiwgJnBzX2JyaWRnZS0+ZndfZmlsZSwgYnVmLCBjb3VudCk7Cj4g KwlpZiAocmV0KQo+ICsJCXJldHVybiByZXQ7Cj4gKwo+ICsJcmV0dXJuIGNvdW50Owo+ICt9Cj4g Kwo+ICsvKiBGaXJtd2FyZSBWZXJzaW9uIGlzIHJldHVybmVkIGFzIE1ham9yLk1pbm9yLkJ1aWxk ICovCj4gK3N0YXRpYyBzc2l6ZV90IHBzODY0MF9md192ZXJzaW9uX3Nob3coc3RydWN0IGRldmlj ZSAqZGV2LAo+ICsJCQkJICAgICAgc3RydWN0IGRldmljZV9hdHRyaWJ1dGUgKmF0dHIsIGNoYXIg KmJ1ZikKPiArewo+ICsJc3RydWN0IHBzODY0MCAqcHNfYnJpZGdlID0gZGV2X2dldF9kcnZkYXRh KGRldik7Cj4gKwlzdHJ1Y3QgcHM4NjQwX2luZm8gKmluZm8gPSAmcHNfYnJpZGdlLT5pbmZvOwo+ ICsKPiArCXJldHVybiBzY25wcmludGYoYnVmLCBQQUdFX1NJWkUsICIldS4ldVxuIiwgaW5mby0+ dmVyc2lvbiA+PiA4LAo+ICsJCQkgaW5mby0+dmVyc2lvbiAmIDB4ZmYpOwo+ICt9Cj4gKwo+ICsv KiBIYXJkd2FyZSBWZXJzaW9uIGlzIHJldHVybmVkIGFzIEZhbWlseUlELlZhcmlhbnRJRCAqLwo+ ICtzdGF0aWMgc3NpemVfdCBwczg2NDBfaHdfdmVyc2lvbl9zaG93KHN0cnVjdCBkZXZpY2UgKmRl diwKPiArCQkJCSAgICAgIHN0cnVjdCBkZXZpY2VfYXR0cmlidXRlICphdHRyLCBjaGFyICpidWYp Cj4gK3sKPiArCXN0cnVjdCBwczg2NDAgKnBzX2JyaWRnZSA9IGRldl9nZXRfZHJ2ZGF0YShkZXYp Owo+ICsJc3RydWN0IHBzODY0MF9pbmZvICppbmZvID0gJnBzX2JyaWRnZS0+aW5mbzsKPiArCj4g KwlyZXR1cm4gc2NucHJpbnRmKGJ1ZiwgUEFHRV9TSVpFLCAicHMldS4ldVxuIiwgaW5mby0+ZmFt aWx5X2lkLAo+ICsJCQkgaW5mby0+dmFyaWFudF9pZCk7Cj4gK30KPiArCj4gK3N0YXRpYyBpbnQg cHM4NjQwX3NwaV9zZW5kX2NtZChzdHJ1Y3QgcHM4NjQwICpwc19icmlkZ2UsIHU4ICpjbWQsIHU4 IGNtZF9sZW4pCj4gK3sKPiArCXN0cnVjdCBpMmNfY2xpZW50ICpjbGllbnQgPSBwc19icmlkZ2Ut PnBhZ2VbMl07Cj4gKwl1OCBpOwo+ICsJaW50IHJldDsKPiArCj4gKwlyZXQgPSBwczg2NDBfcmVn d19ieXRlKGNsaWVudCwgUEFHRTJfSVJPTV9DVFJMLCBJUk9NX0VOQUJMRSk7Cj4gKwlpZiAocmV0 KQo+ICsJCWdvdG8gZXJyOwo+ICsKPiArCS8qIHdyaXRlIGNvbW1hbmQgaW4gd3JpdGUgcG9ydCAq Lwo+ICsJZm9yIChpID0gMDsgaSA8IGNtZF9sZW47IGkrKykgewo+ICsJCXJldCA9IHBzODY0MF9y ZWd3X2J5dGUoY2xpZW50LCBQQUdFMl9TV1NQSV9XREFUQSwgY21kW2ldKTsKPiArCQlpZiAocmV0 KQo+ICsJCQlnb3RvIGVycjsKPiArCX0KPiArCS8qCWNvbW1hbmQgbGVuZ3RoICovCj4gKwlyZXQg PSBwczg2NDBfcmVnd19ieXRlKGNsaWVudCwgUEFHRTJfU1dTUElfTEVOLCBjbWRfbGVuIC0gMSk7 Cj4gKwlpZiAocmV0KQo+ICsJCWdvdG8gZXJyOwo+ICsJLyoJdHJpZ2dlciByZWFkICovCj4gKwly ZXQgPSBwczg2NDBfcmVnd19ieXRlKGNsaWVudCwgUEFHRTJfU1dTUElfQ1RMLCBUUklHR0VSX05P X1JFQURCQUNLKTsKPiArCWlmIChyZXQpCj4gKwkJZ290byBlcnI7Cj4gKwo+ICsJcmV0ID0gcHM4 NjQwX3JlZ3dfYnl0ZShjbGllbnQsIFBBR0UyX0lST01fQ1RSTCwgSVJPTV9ESVNBQkxFKTsKPiAr CWlmIChyZXQpCj4gKwkJZ290byBlcnI7Cj4gKwo+ICsJcmV0dXJuIDA7Cj4gKwo+ICtlcnI6Cj4g KwlkZXZfZXJyKCZjbGllbnQtPmRldiwgInNlbmQgY29tbWFuZCBlcnI6ICVkXG4iLCByZXQpOwo+ ICsJcmV0dXJuIHJldDsKPiArfQo+ICsKPiArc3RhdGljIGludCBwczg2NDBfd2FpdF9zcGlfcmVh ZHkoc3RydWN0IHBzODY0MCAqcHNfYnJpZGdlKQo+ICt7Cj4gKwlzdHJ1Y3QgaTJjX2NsaWVudCAq Y2xpZW50ID0gcHNfYnJpZGdlLT5wYWdlWzJdOwo+ICsJdTggc3BpX3JkeV9zdCwgcmV0cnlfY250 ID0gMDsKPiArCj4gKwlkbyB7Cj4gKwkJcHM4NjQwX3JlZ3IoY2xpZW50LCBQQUdFMl9TUElfU1RB VFVTLCAmc3BpX3JkeV9zdCwgMSk7Cj4gKwkJbXNsZWVwKDIwKTsKPiArCQlpZiAoKHJldHJ5X2Nu dCA9PSBTUElfTUFYX1JFVFJZX0NOVCkgJiYKPiArCQkgICAgKChzcGlfcmR5X3N0ICYgMHgwYykg IT0gMHgwYykpIHsKPiArCQkJZGV2X2VycigmY2xpZW50LT5kZXYsICJ3YWl0IHNwaSByZWFkeSB0 aW1lb3V0XG4iKTsKPiArCQkJcmV0dXJuIC1FQlVTWTsKPiArCQl9Cj4gKwl9IHdoaWxlICgocmV0 cnlfY250KysgPCBTUElfTUFYX1JFVFJZX0NOVCkgJiYKPiArCQkgKHNwaV9yZHlfc3QgJiAweDBj KSA9PSAweDBjKTsKPiArCj4gKwlyZXR1cm4gMDsKPiArfQo+ICsKPiArc3RhdGljIGludCBwczg2 NDBfd2FpdF9zcGlfbm9idXN5KHN0cnVjdCBwczg2NDAgKnBzX2JyaWRnZSkKPiArewo+ICsJc3Ry dWN0IGkyY19jbGllbnQgKmNsaWVudCA9IHBzX2JyaWRnZS0+cGFnZVsyXTsKPiArCXU4IHNwaV9z dGF0dXMsIHJldHJ5X2NudCA9IDA7Cj4gKwlpbnQgcmV0Owo+ICsKPiArCWRvIHsKPiArCQkvKiAg MHgwNSBSRFNSOyBSZWFkLVN0YXR1cy1SZWdpc3RlciAqLwo+ICsJCXJldCA9IHBzODY0MF9yZWd3 X2J5dGUoY2xpZW50LCBQQUdFMl9TV1NQSV9XREFUQSwgMHgwNSk7Cj4gKwkJaWYgKHJldCkKPiAr CQkJZ290byBlcnJfc2VuZF9jbWRfZXhpdDsKPiArCj4gKwkJLyogIGNvbW1hbmQgbGVuZ3RoICov Cj4gKwkJcmV0ID0gcHM4NjQwX3JlZ3dfYnl0ZShjbGllbnQsIFBBR0UyX1NXU1BJX0xFTiwgMHgw MCk7Cj4gKwkJaWYgKHJldCkKPiArCQkJZ290byBlcnJfc2VuZF9jbWRfZXhpdDsKPiArCj4gKwkJ LyogIHRyaWdnZXIgcmVhZCAqLwo+ICsJCXJldCA9IHBzODY0MF9yZWd3X2J5dGUoY2xpZW50LCBQ QUdFMl9TV1NQSV9DVEwsCj4gKwkJCQkgICAgICAgVFJJR0dFUl9SRUFEQkFDSyk7Cj4gKwkJaWYg KHJldCkKPiArCQkJZ290byBlcnJfc2VuZF9jbWRfZXhpdDsKPiArCj4gKwkJLyogZGVsYXkgZm9y IGNtZCBzZW5kICovCj4gKwkJdXNsZWVwX3JhbmdlKDEwMCwgMzAwKTsKPiArCQkvKiB3YWl0IGZv ciBTUEkgUk9NIHVudGlsIG5vdCBidXN5ICovCj4gKwkJcmV0ID0gcHM4NjQwX3JlZ3IoY2xpZW50 LCBQQUdFMl9TV1NQSV9SREFUQSwgJnNwaV9zdGF0dXMsIDEpOwo+ICsJCWlmIChyZXQpCj4gKwkJ CWdvdG8gZXJyX3NlbmRfY21kX2V4aXQ7Cj4gKwl9IHdoaWxlICgocmV0cnlfY250KysgPCBTUElf TUFYX1JFVFJZX0NOVCkgJiYKPiArCQkgKHNwaV9zdGF0dXMgJiAweDBjKSA9PSAweDBjKTsKPiAr Cj4gKwlpZiAoKHJldHJ5X2NudCA+IFNQSV9NQVhfUkVUUllfQ05UKSAmJiAoc3BpX3N0YXR1cyAm IDB4MGMpICE9IDB4MGMpIHsKPiArCQlyZXQgPSAtRUJVU1k7Cj4gKwkJZGV2X2VycigmY2xpZW50 LT5kZXYsICJ3YWl0IHNwaSBubyBidXN5IHRpbWVvdXQ6ICVkXG4iLCByZXQpOwo+ICsJCWdvdG8g ZXJyX3RpbWVvdXQ7Cj4gKwl9Cj4gKwo+ICsJcmV0dXJuIDA7Cj4gKwo+ICtlcnJfc2VuZF9jbWRf ZXhpdDoKPiArCWRldl9lcnIoJmNsaWVudC0+ZGV2LCAic2VuZCBjb21tYW5kIGVycjogJWRcbiIs IHJldCk7Cj4gKwo+ICtlcnJfdGltZW91dDoKPiArCXJldHVybiByZXQ7Cj4gK30KPiArCj4gK3N0 YXRpYyBpbnQgcHM4NjQwX3dhaXRfcm9tX2lkbGUoc3RydWN0IHBzODY0MCAqcHNfYnJpZGdlKQo+ ICt7Cj4gKwlzdHJ1Y3QgaTJjX2NsaWVudCAqY2xpZW50ID0gcHNfYnJpZGdlLT5wYWdlWzBdOwo+ ICsJaW50IHJldDsKPiArCj4gKwlyZXQgPSBwczg2NDBfcmVnd19ieXRlKGNsaWVudCwgUEFHRTJf SVJPTV9DVFJMLCBJUk9NX0VOQUJMRSk7Cj4gKwlpZiAocmV0KQo+ICsJCWdvdG8gZXhpdDsKPiAr Cj4gKwlyZXQgPSBwczg2NDBfd2FpdF9zcGlfcmVhZHkocHNfYnJpZGdlKTsKPiArCWlmIChyZXQp Cj4gKwkJZ290byBleGl0Owo+ICsKPiArCXJldCA9IHBzODY0MF93YWl0X3NwaV9ub2J1c3kocHNf YnJpZGdlKTsKPiArCWlmIChyZXQpCj4gKwkJZ290byBleGl0Owo+ICsKPiArCXJldCA9IHBzODY0 MF9yZWd3X2J5dGUoY2xpZW50LCBQQUdFMl9JUk9NX0NUUkwsIElST01fRElTQUJMRSk7Cj4gKwlp ZiAocmV0KQo+ICsJCWdvdG8gZXhpdDsKPiArCj4gK2V4aXQ6Cj4gKwlpZiAocmV0KQo+ICsJCWRl dl9lcnIoJmNsaWVudC0+ZGV2LCAid2FpdCBwczg2NDAgcm9tIGlkbGUgZmFpbDogJWRcbiIsIHJl dCk7Cj4gKwo+ICsJcmV0dXJuIHJldDsKPiArfQo+ICsKPiArc3RhdGljIGludCBwczg2NDBfc3Bp X2RsX21vZGUoc3RydWN0IHBzODY0MCAqcHNfYnJpZGdlKQo+ICt7Cj4gKwlzdHJ1Y3QgaTJjX2Ns aWVudCAqY2xpZW50ID0gcHNfYnJpZGdlLT5wYWdlWzJdOwo+ICsJaW50IHJldDsKPiArCj4gKwkv KiBzd2l0Y2ggcHM4NjQwIG1vZGUgdG8gc3BpIGRsIG1vZGUgKi8KPiArCWdwaW9kX3NldF92YWx1 ZShwc19icmlkZ2UtPmdwaW9fbW9kZV9zZWxfbiwgMCk7Cj4gKwo+ICsJLyogcmVzZXQgc3BpIGlu dGVyZmFjZSAqLwo+ICsJcmV0ID0gcHM4NjQwX3JlZ3dfYnl0ZShjbGllbnQsIFBBR0UyX1NXX1JF U1QsIDB4YzApOwo+ICsJaWYgKHJldCkKPiArCQlnb3RvIGV4aXQ7Cj4gKwo+ICsJcmV0ID0gcHM4 NjQwX3JlZ3dfYnl0ZShjbGllbnQsIFBBR0UyX1NXX1JFU1QsIDB4NDApOwo+ICsJaWYgKHJldCkK PiArCQlnb3RvIGV4aXQ7Cj4gKwo+ICtleGl0Ogo+ICsJaWYgKHJldCkKPiArCQlkZXZfZXJyKCZj bGllbnQtPmRldiwgImZhaWwgcmVzZXQgc3BpIGludGVyZmFjZTogJWRcbiIsIHJldCk7Cj4gKwo+ ICsJcmV0dXJuIHJldDsKPiArfQo+ICsKPiArc3RhdGljIGludCBwczg2NDBfcm9tX3ByZXBhcmUo c3RydWN0IHBzODY0MCAqcHNfYnJpZGdlKQo+ICt7Cj4gKwlzdHJ1Y3QgaTJjX2NsaWVudCAqY2xp ZW50ID0gcHNfYnJpZGdlLT5wYWdlWzJdOwo+ICsJc3RydWN0IGRldmljZSAqZGV2ID0gJmNsaWVu dC0+ZGV2Owo+ICsJdTggaSwgY21kWzJdOwo+ICsJaW50IHJldDsKPiArCj4gKwkvKglFbmFibGUt V3JpdGUtU3RhdHVzLVJlZ2lzdGVyICovCj4gKwljbWRbMF0gPSAweDA2Owo+ICsJcmV0ID0gcHM4 NjQwX3NwaV9zZW5kX2NtZChwc19icmlkZ2UsIGNtZCwgMSk7Cj4gKwlpZiAocmV0KSB7Cj4gKwkJ ZGV2X2VycihkZXYsICJmYWlsZWQgZW5hYmxlLXdyaXRlLXN0YXR1cy1yZWdpc3RlcjogJWRcbiIs IHJldCk7Cj4gKwkJcmV0dXJuIHJldDsKPiArCX0KPiArCj4gKwkvKiBkaXNhYmxlIGFsbCBwcm90 ZWN0aW9uICovCj4gKwljbWRbMF0gPSAweDAxOwo+ICsJY21kWzFdID0gMHgwMDsKPiArCXJldCA9 IHBzODY0MF9zcGlfc2VuZF9jbWQocHNfYnJpZGdlLCBjbWQsIDIpOwo+ICsJaWYgKHJldCkgewo+ ICsJCWRldl9lcnIoZGV2LCAiZmFpbCBkaXNhYmxlIGFsbCBwcm90ZWN0aW9uOiAlZFxuIiwgcmV0 KTsKPiArCQlyZXR1cm4gcmV0Owo+ICsJfQo+ICsKPiArCS8qIHdhaXQgZm9yIFNQSSBtb2R1bGUg cmVhZHkgKi8KPiArCXJldCA9IHBzODY0MF93YWl0X3JvbV9pZGxlKHBzX2JyaWRnZSk7Cj4gKwlp ZiAocmV0KSB7Cj4gKwkJZGV2X2VycihkZXYsICJmYWlsIHdhaXQgcm9tIGlkbGU6ICVkXG4iLCBy ZXQpOwo+ICsJCXJldHVybiByZXQ7Cj4gKwl9Cj4gKwo+ICsJcHM4NjQwX3JlZ3dfYnl0ZShjbGll bnQsIFBBR0UyX0lST01fQ1RSTCwgSVJPTV9FTkFCTEUpOwo+ICsJZm9yIChpID0gMDsgaSA8IDY7 IGkrKykKPiArCQlwczg2NDBfcmVnd19ieXRlKGNsaWVudCwgUEFHRTJfRU5DVExTUElfV1IsIGVu Y19jdHJsX2NvZGVbaV0pOwo+ICsJcHM4NjQwX3JlZ3dfYnl0ZShjbGllbnQsIFBBR0UyX0lST01f Q1RSTCwgSVJPTV9ESVNBQkxFKTsKPiArCj4gKwkvKglFbmFibGUtV3JpdGUtU3RhdHVzLVJlZ2lz dGVyICovCj4gKwljbWRbMF0gPSAweDA2Owo+ICsJcmV0ID0gcHM4NjQwX3NwaV9zZW5kX2NtZChw c19icmlkZ2UsIGNtZCwgMSk7Cj4gKwlpZiAocmV0KSB7Cj4gKwkJZGV2X2VycihkZXYsICJmYWls IGVuYWJsZS13cml0ZS1zdGF0dXMtcmVnaXN0ZXI6ICVkXG4iLCByZXQpOwo+ICsJCXJldHVybiBy ZXQ7Cj4gKwl9Cj4gKwo+ICsJLyogY2hpcCBlcmFzZSBjb21tYW5kICovCj4gKwljbWRbMF0gPSAw eGM3Owo+ICsJcmV0ID0gcHM4NjQwX3NwaV9zZW5kX2NtZChwc19icmlkZ2UsIGNtZCwgMSk7Cj4g KwlpZiAocmV0KSB7Cj4gKwkJZGV2X2VycihkZXYsICJmYWlsIGRpc2FibGUgYWxsIHByb3RlY3Rp b246ICVkXG4iLCByZXQpOwo+ICsJCXJldHVybiByZXQ7Cj4gKwl9Cj4gKwo+ICsJcmV0ID0gcHM4 NjQwX3dhaXRfcm9tX2lkbGUocHNfYnJpZGdlKTsKPiArCWlmIChyZXQpIHsKPiArCQlkZXZfZXJy KGRldiwgImZhaWwgd2FpdCByb20gaWRsZTogJWRcbiIsIHJldCk7Cj4gKwkJcmV0dXJuIHJldDsK PiArCX0KPiArCj4gKwlyZXR1cm4gMDsKPiArfQo+ICsKPiArc3RhdGljIGludCBwczg2NDBfdmFs aWRhdGVfZmlybXdhcmUoc3RydWN0IHBzODY0MCAqcHNfYnJpZGdlLAo+ICsJCQkJICAgIGNvbnN0 IHN0cnVjdCBmaXJtd2FyZSAqZncpCj4gK3sKPiArCXN0cnVjdCBpMmNfY2xpZW50ICpjbGllbnQg PSBwc19icmlkZ2UtPnBhZ2VbMF07Cj4gKwlzdHJ1Y3QgcHM4NjQwX2luZm8gKmluZm8gPSAmcHNf YnJpZGdlLT5pbmZvOwo+ICsJdTE2IGZ3X2NoaXBfaWQsIGZ3X3ZlcnNpb25faWQ7Cj4gKwo+ICsJ LyoKPiArCSAqIEdldCB0aGUgY2hpcF9pZCBmcm9tIHRoZSBmaXJtd2FyZS4gTWFrZSBzdXJlIHRo YXQgaXQgaXMgdGhlCj4gKwkgKiByaWdodCBjb250cm9sbGVyIHRvIGRvIHRoZSBmaXJtd2FyZSBh bmQgY29uZmlnIHVwZGF0ZS4KPiArCSAqLwo+ICsJZndfY2hpcF9pZCA9IGdldF91bmFsaWduZWRf bGUxNihmdy0+ZGF0YSArIEZXX0NISVBfSURfT0ZGU0VUKTsKPiArCj4gKwlpZiAoZndfY2hpcF9p ZCAhPSAweDg2NDApIHsKPiArCQlkZXZfZXJyKCZjbGllbnQtPmRldiwKPiArCQkJImNoaXAgaWQg bWlzbWF0Y2g6IGZ3IDB4JXggdnMuIGNoaXAgMHg4NjQwXG4iLAo+ICsJCQlmd19jaGlwX2lkKTsK PiArCQlyZXR1cm4gLUVOT0RFVjsKPiArCX0KPiArCj4gKwlmd192ZXJzaW9uX2lkID0gZ2V0X3Vu YWxpZ25lZF9sZTE2KGZ3LT5kYXRhICsgRldfVkVSU0lPTl9PRkZTRVQpOwo+ICsKPiArCWlmIChm d19jaGlwX2lkICE9IGluZm8tPnZlcnNpb24pIHsKPiArCQlkZXZfZXJyKCZjbGllbnQtPmRldiwK PiArCQkJImZ3IHZlcnNpb24gbWlzbWF0Y2g6IGZ3ICVkLiVkIHZzLiBjaGlwICVkLiVkXG4iLAo+ ICsJCQlmd192ZXJzaW9uX2lkID4+IDgsIGZ3X3ZlcnNpb25faWQgJiAweGZmLAo+ICsJCQlpbmZv LT52ZXJzaW9uID4+IDgsIGluZm8tPnZlcnNpb24gJiAweGZmKTsKPiArCQlyZXR1cm4gLUVOT0RF VjsKPiArCX0KPiArCj4gKwlyZXR1cm4gMDsKPiArfQo+ICsKPiArc3RhdGljIGludCBwczg2NDBf d3JpdGVfcm9tKHN0cnVjdCBwczg2NDAgKnBzX2JyaWRnZSwgY29uc3Qgc3RydWN0IGZpcm13YXJl ICpmdykKPiArewo+ICsJc3RydWN0IGkyY19jbGllbnQgKmNsaWVudCA9IHBzX2JyaWRnZS0+cGFn ZVswXTsKPiArCXN0cnVjdCBkZXZpY2UgKmRldiA9ICZjbGllbnQtPmRldjsKPiArCXN0cnVjdCBp MmNfY2xpZW50ICpjbGllbnQyID0gcHNfYnJpZGdlLT5wYWdlWzJdOwo+ICsJc3RydWN0IGkyY19j bGllbnQgKmNsaWVudDcgPSBwc19icmlkZ2UtPnBhZ2VbN107Cj4gKwl1bnNpZ25lZCBpbnQgcG9z ID0gMDsKPiArCXU4IHByb2dyZXNzX2NudCwgcm9tX3BhZ2VfaWRbMl07Cj4gKwlpbnQgcmV0Owo+ ICsKPiArCXBzODY0MF9yZWd3X2J5dGUoY2xpZW50MiwgUEFHRTJfU1BJX0NGRzMsIEkyQ19UT19T UElfUkVTRVQpOwo+ICsJbXNsZWVwKDEwMCk7Cj4gKwlwczg2NDBfcmVnd19ieXRlKGNsaWVudDIs IFBBR0UyX1NQSV9DRkczLCAweDAwKTsKPiArCj4gKwl3aGlsZSAocG9zIDwgZnctPnNpemUpIHsK PiArCQlyb21fcGFnZV9pZFswXSA9IChwb3MgPj4gOCkgJiAweEZGOwo+ICsJCXJvbV9wYWdlX2lk WzFdID0gKHBvcyA+PiAxNikgJiAweEZGOwo+ICsJCXJldCA9IHBzODY0MF9yZWd3KGNsaWVudDIs IFBBR0UyX1JPTUFERF9CWVRFMSwgcm9tX3BhZ2VfaWQsIDIpOwo+ICsJCWlmIChyZXQpCj4gKwkJ CWdvdG8gZXJyb3I7Cj4gKwkJcmV0ID0gcHM4NjQwX3JlZ3coY2xpZW50NywgMCwgZnctPmRhdGEg KyBwb3MsIDI1Nik7Cj4gKwkJaWYgKHJldCkKPiArCQkJZ290byBlcnJvcjsKPiArCj4gKwkJcG9z ICs9IDI1NjsKPiArCQlpZiAocHJvZ3Jlc3NfY250ICE9IChwb3MgKiAxMDApIC8gZnctPnNpemUp IHsKPiArCQkJcHJvZ3Jlc3NfY250ID0gKHBvcyAqIDEwMCkgLyBmdy0+c2l6ZTsKPiArCQkJZGV2 X2luZm8oZGV2LCAiZncgdXBkYXRlIHByb2dyZXNzIHBlcmNlbnQgJWRcbiIsCj4gKwkJCQkgcHJv Z3Jlc3NfY250KTsKPiArCQl9Cj4gKwl9Cj4gKwlyZXR1cm4gMDsKPiArCj4gK2Vycm9yOgo+ICsJ ZGV2X2VycihkZXYsICJmYWlsZWQgd3JpdGUgZXh0ZW5hbCBmbGFzaCwgJWRcbiIsIHJldCk7Cj4g KwlyZXR1cm4gcmV0Owo+ICt9Cj4gKwo+ICtzdGF0aWMgaW50IHBzODY0MF9zcGlfbm9ybWFsX21v ZGUoc3RydWN0IHBzODY0MCAqcHNfYnJpZGdlKQo+ICt7Cj4gKwl1OCBjbWRbMl07Cj4gKwlzdHJ1 Y3QgaTJjX2NsaWVudCAqY2xpZW50ID0gcHNfYnJpZGdlLT5wYWdlWzJdOwo+ICsKPiArCS8qICBF bmFibGUtV3JpdGUtU3RhdHVzLVJlZ2lzdGVyICovCj4gKwljbWRbMF0gPSAweDA2Owo+ICsJcHM4 NjQwX3NwaV9zZW5kX2NtZChwc19icmlkZ2UsIGNtZCwgMSk7Cj4gKwo+ICsJLyogcHJvdGVjdCBC UEwvQlAwL0JQMSAqLwo+ICsJY21kWzBdID0gMHgwMTsKPiArCWNtZFsxXSA9IDB4OGM7Cj4gKwlw czg2NDBfc3BpX3NlbmRfY21kKHBzX2JyaWRnZSwgY21kLCAyKTsKPiArCj4gKwkvKiB3YWl0IGZv ciBTUEkgcm9tIHJlYWR5ICovCj4gKwlwczg2NDBfd2FpdF9yb21faWRsZShwc19icmlkZ2UpOwo+ ICsKPiArICAgICAvKiBkaXNhYmxlIFBTODY0MCBtYXBwaW5nIGZ1bmN0aW9uICovCj4gKwlwczg2 NDBfcmVnd19ieXRlKGNsaWVudCwgUEFHRTJfRU5DVExTUElfV1IsIDB4MDApOwo+ICsKPiArCWdw aW9kX3NldF92YWx1ZShwc19icmlkZ2UtPmdwaW9fbW9kZV9zZWxfbiwgMSk7Cj4gKwlyZXR1cm4g MDsKPiArfQo+ICsKPiArc3RhdGljIGludCBwczg2NDBfZW50ZXJfYmwoc3RydWN0IHBzODY0MCAq cHNfYnJpZGdlKQo+ICt7Cj4gKwlpbnQgcmV0Owo+ICsKPiArCXJldCA9IHBzODY0MF9zcGlfZGxf bW9kZShwc19icmlkZ2UpOwo+ICsJaWYgKHJldCkKPiArCQlyZXR1cm4gcmV0Owo+ICsKPiArCXBz X2JyaWRnZS0+aW5fZndfdXBkYXRlID0gdHJ1ZTsKPiArCXJldHVybiAwOwo+ICt9Cj4gKwo+ICtz dGF0aWMgdm9pZCBwczg2NDBfZXhpdF9ibChzdHJ1Y3QgcHM4NjQwICpwc19icmlkZ2UsIGNvbnN0 IHN0cnVjdCBmaXJtd2FyZSAqZncpCj4gK3sKPiArCXBzX2JyaWRnZS0+aW5fZndfdXBkYXRlID0g ZmFsc2U7Cj4gKwlwczg2NDBfc3BpX25vcm1hbF9tb2RlKHBzX2JyaWRnZSk7Cj4gK30KPiArCj4g K3N0YXRpYyBpbnQgcHM4NjQwX2xvYWRfZncoc3RydWN0IHBzODY0MCAqcHNfYnJpZGdlLCBjb25z dCBzdHJ1Y3QgZmlybXdhcmUgKmZ3KQo+ICt7Cj4gKwlzdHJ1Y3QgaTJjX2NsaWVudCAqY2xpZW50 ID0gcHNfYnJpZGdlLT5wYWdlWzBdOwo+ICsJc3RydWN0IGRldmljZSAqZGV2ID0gJmNsaWVudC0+ ZGV2Owo+ICsJaW50IHJldDsKPiArCWJvb2wgcHM4NjQwX3N0YXR1c19iYWNrdXAgPSBwc19icmlk Z2UtPmVuYWJsZWQ7Cj4gKwo+ICsJaWYgKCFwczg2NDBfaW5fYm9vdGxvYWRlcihwc19icmlkZ2Up KSB7Cj4gKwkJaWYgKCFwczg2NDBfc3RhdHVzX2JhY2t1cCkKPiArCQkJcHM4NjQwX3ByZXBhcmUo cHNfYnJpZGdlKTsKPiArCj4gKwkJcmV0ID0gcHM4NjQwX2VudGVyX2JsKHBzX2JyaWRnZSk7Cj4g KwkJaWYgKHJldCkKPiArCQkJZ290byBleGl0Owo+ICsJfQo+ICsKPiArCXJldCA9IHBzODY0MF92 YWxpZGF0ZV9maXJtd2FyZShwc19icmlkZ2UsIGZ3KTsKPiArCWlmIChyZXQpCj4gKwkJZ290byBl eGl0Owo+ICsKPiArCXJldCA9IHBzODY0MF9yb21fcHJlcGFyZShwc19icmlkZ2UpOwo+ICsJaWYg KHJldCkKPiArCQlnb3RvIGV4aXQ7Cj4gKwo+ICsJcmV0ID0gcHM4NjQwX3dyaXRlX3JvbShwc19i cmlkZ2UsIGZ3KTsKPiArCj4gK2V4aXQ6Cj4gKwlpZiAocmV0KQo+ICsJCWRldl9lcnIoZGV2LCAi RmFpbGVkIHRvIGxvYWQgZmlybXdhcmUsICVkXG4iLCByZXQpOwo+ICsKPiArCXBzODY0MF9leGl0 X2JsKHBzX2JyaWRnZSwgZncpOwo+ICsJaWYgKCFwczg2NDBfc3RhdHVzX2JhY2t1cCkKPiArCQlw czg2NDBfZGlzYWJsZSgmcHNfYnJpZGdlLT5icmlkZ2UpOwo+ICsJcmV0dXJuIHJldDsKPiArfQo+ ICsKPiArc3RhdGljIHNzaXplX3QgcHM4NjQwX3VwZGF0ZV9md19zdG9yZShzdHJ1Y3QgZGV2aWNl ICpkZXYsCj4gKwkJCQkgICAgICBzdHJ1Y3QgZGV2aWNlX2F0dHJpYnV0ZSAqYXR0ciwKPiArCQkJ CSAgICAgIGNvbnN0IGNoYXIgKmJ1Ziwgc2l6ZV90IGNvdW50KQo+ICt7Cj4gKwlzdHJ1Y3QgaTJj X2NsaWVudCAqY2xpZW50ID0gdG9faTJjX2NsaWVudChkZXYpOwo+ICsJc3RydWN0IHBzODY0MCAq cHNfYnJpZGdlID0gaTJjX2dldF9jbGllbnRkYXRhKGNsaWVudCk7Cj4gKwljb25zdCBzdHJ1Y3Qg ZmlybXdhcmUgKmZ3Owo+ICsJaW50IGVycm9yOwo+ICsKPiArCWVycm9yID0gcmVxdWVzdF9maXJt d2FyZSgmZncsIHBzX2JyaWRnZS0+ZndfZmlsZSwgZGV2KTsKPiArCWlmIChlcnJvcikgewo+ICsJ CWRldl9lcnIoZGV2LCAiVW5hYmxlIHRvIG9wZW4gZmlybXdhcmUgJXM6ICVkXG4iLAo+ICsJCQlw c19icmlkZ2UtPmZ3X2ZpbGUsIGVycm9yKTsKPiArCQlyZXR1cm4gZXJyb3I7Cj4gKwl9Cj4gKwo+ ICsJZXJyb3IgPSBwczg2NDBfbG9hZF9mdyhwc19icmlkZ2UsIGZ3KTsKPiArCWlmIChlcnJvcikK PiArCQlkZXZfZXJyKGRldiwgIlRoZSBmaXJtd2FyZSB1cGRhdGUgZmFpbGVkKCVkKVxuIiwgZXJy b3IpOwo+ICsJZWxzZQo+ICsJCWRldl9pbmZvKGRldiwgIlRoZSBmaXJtd2FyZSB1cGRhdGUgc3Vj Y2VlZGVkXG4iKTsKPiArCj4gKwlyZWxlYXNlX2Zpcm13YXJlKGZ3KTsKPiArCXJldHVybiBlcnJv ciA/IGVycm9yIDogY291bnQ7Cj4gK30KPiArCj4gK3N0YXRpYyBERVZJQ0VfQVRUUihmd19maWxl LCBTX0lSVUdPIHwgU19JV1VTUiwgcHM4NjQwX2Z3X2ZpbGVfc2hvdywKPiArCQkgICBwczg2NDBf ZndfZmlsZV9zdG9yZSk7Cj4gK3N0YXRpYyBERVZJQ0VfQVRUUihmd192ZXJzaW9uLCBTX0lSVUdP LCBwczg2NDBfZndfdmVyc2lvbl9zaG93LCBOVUxMKTsKPiArc3RhdGljIERFVklDRV9BVFRSKGh3 X3ZlcnNpb24sIFNfSVJVR08sIHBzODY0MF9od192ZXJzaW9uX3Nob3csIE5VTEwpOwo+ICtzdGF0 aWMgREVWSUNFX0FUVFIodXBkYXRlX2Z3LCBTX0lXVVNSLCBOVUxMLCBwczg2NDBfdXBkYXRlX2Z3 X3N0b3JlKTsKPiArCj4gK3N0YXRpYyBzdHJ1Y3QgYXR0cmlidXRlICpwczg2NDBfYXR0cnNbXSA9 IHsKPiArCSZkZXZfYXR0cl9md19maWxlLmF0dHIsCj4gKwkmZGV2X2F0dHJfZndfdmVyc2lvbi5h dHRyLAo+ICsJJmRldl9hdHRyX2h3X3ZlcnNpb24uYXR0ciwKPiArCSZkZXZfYXR0cl91cGRhdGVf ZncuYXR0ciwKPiArCU5VTEwKPiArfTsKPiArCj4gK3N0YXRpYyBjb25zdCBzdHJ1Y3QgYXR0cmli dXRlX2dyb3VwIHBzODY0MF9hdHRyX2dyb3VwID0gewo+ICsJLmF0dHJzID0gcHM4NjQwX2F0dHJz LAo+ICt9Owo+ICsKPiArc3RhdGljIHZvaWQgcHM4NjQwX3JlbW92ZV9zeXNmc19ncm91cCh2b2lk ICpkYXRhKQo+ICt7Cj4gKwlzdHJ1Y3QgcHM4NjQwICpwc19icmlkZ2UgPSBkYXRhOwo+ICsKPiAr CXN5c2ZzX3JlbW92ZV9ncm91cCgmcHNfYnJpZGdlLT5wYWdlWzBdLT5kZXYua29iaiwgJnBzODY0 MF9hdHRyX2dyb3VwKTsKPiArfQo+ICsKPiArLyogZm9yIGZpcmV3YXJlIHVwZGF0ZSBlbmQgKi8K PiArCj4gK3N0YXRpYyBpbnQgcHM4NjQwX3Byb2JlKHN0cnVjdCBpMmNfY2xpZW50ICpjbGllbnQs Cj4gKwkJCWNvbnN0IHN0cnVjdCBpMmNfZGV2aWNlX2lkICppZCkKPiArewo+ICsJc3RydWN0IGRl dmljZSAqZGV2ID0gJmNsaWVudC0+ZGV2Owo+ICsJc3RydWN0IHBzODY0MCAqcHNfYnJpZGdlOwo+ ICsJc3RydWN0IGRldmljZV9ub2RlICpucCA9IGRldi0+b2Zfbm9kZTsKPiArCXN0cnVjdCBkZXZp Y2Vfbm9kZSAqcG9ydCwgKm91dF9lcDsKPiArCXN0cnVjdCBkZXZpY2Vfbm9kZSAqcGFuZWxfbm9k ZSA9IE5VTEw7Cj4gKwlpbnQgaSwgcmV0Owo+ICsKPiArCXBzX2JyaWRnZSA9IGRldm1fa3phbGxv YyhkZXYsIHNpemVvZigqcHNfYnJpZGdlKSwgR0ZQX0tFUk5FTCk7Cj4gKwlpZiAoIXBzX2JyaWRn ZSkgewo+ICsJCXJldCA9IC1FTk9NRU07Cj4gKwkJZ290byBleGl0Owo+ICsJfQo+ICsJLyogcG9y dEAxIGlzIHBzODY0MCBvdXRwdXQgcG9ydCAqLwo+ICsJcG9ydCA9IG9mX2dyYXBoX2dldF9wb3J0 X2J5X2lkKG5wLCAxKTsKPiArCWlmIChwb3J0KSB7Cj4gKwkJb3V0X2VwID0gb2ZfZ2V0X2NoaWxk X2J5X25hbWUocG9ydCwgImVuZHBvaW50Iik7Cj4gKwkJb2Zfbm9kZV9wdXQocG9ydCk7Cj4gKwkJ aWYgKG91dF9lcCkgewo+ICsJCQlwYW5lbF9ub2RlID0gb2ZfZ3JhcGhfZ2V0X3JlbW90ZV9wb3J0 X3BhcmVudChvdXRfZXApOwo+ICsJCQlvZl9ub2RlX3B1dChvdXRfZXApOwo+ICsJCX0KPiArCX0K PiArCWlmIChwYW5lbF9ub2RlKSB7Cj4gKwkJcHNfYnJpZGdlLT5wYW5lbCA9IG9mX2RybV9maW5k X3BhbmVsKHBhbmVsX25vZGUpOwo+ICsJCW9mX25vZGVfcHV0KHBhbmVsX25vZGUpOwo+ICsJCWlm ICghcHNfYnJpZGdlLT5wYW5lbCkgewo+ICsJCQlyZXQgPSAtRVBST0JFX0RFRkVSOwo+ICsJCQln b3RvIGV4aXQ7Cj4gKwkJfQo+ICsJfQo+ICsKPiArCXBzX2JyaWRnZS0+cHdyXzN2M19zdXBwbHkg PSBkZXZtX3JlZ3VsYXRvcl9nZXQoZGV2LCAidmRkMzMiKTsKPiArCWlmIChJU19FUlIocHNfYnJp ZGdlLT5wd3JfM3YzX3N1cHBseSkpIHsKPiArCQlyZXQgPSBQVFJfRVJSKHBzX2JyaWRnZS0+cHdy XzN2M19zdXBwbHkpOwo+ICsJCWRldl9lcnIoZGV2LCAiY2Fubm90IGdldCB2ZGQzMyBzdXBwbHk6 ICVkXG4iLCByZXQpOwo+ICsJCWdvdG8gZXhpdDsKPiArCX0KPiArCj4gKwlwc19icmlkZ2UtPnB3 cl8xdjJfc3VwcGx5ID0gZGV2bV9yZWd1bGF0b3JfZ2V0KGRldiwgInZkZDEyIik7Cj4gKwlpZiAo SVNfRVJSKHBzX2JyaWRnZS0+cHdyXzF2Ml9zdXBwbHkpKSB7Cj4gKwkJcmV0ID0gUFRSX0VSUihw c19icmlkZ2UtPnB3cl8xdjJfc3VwcGx5KTsKPiArCQlkZXZfZXJyKGRldiwgImNhbm5vdCBnZXQg dmRkMTIgc3VwcGx5OiAlZFxuIiwgcmV0KTsKPiArCQlnb3RvIGV4aXQ7Cj4gKwl9Cj4gKwo+ICsJ cHNfYnJpZGdlLT5ncGlvX21vZGVfc2VsX24gPSBkZXZtX2dwaW9kX2dldCgmY2xpZW50LT5kZXYs ICJtb2RlLXNlbCIsCj4gKwkJCQkJCSAgICBHUElPRF9PVVRfSElHSCk7Cj4gKwlpZiAoSVNfRVJS KHBzX2JyaWRnZS0+Z3Bpb19tb2RlX3NlbF9uKSkgewo+ICsJCXJldCA9IFBUUl9FUlIocHNfYnJp ZGdlLT5ncGlvX21vZGVfc2VsX24pOwo+ICsJCWRldl9lcnIoZGV2LCAiY2Fubm90IGdldCBncGlv X21vZGVfc2VsX24gJWRcbiIsIHJldCk7Cj4gKwkJZ290byBleGl0Owo+ICsJfQo+ICsKPiArCXBz X2JyaWRnZS0+Z3Bpb19zbHBfbiA9IGRldm1fZ3Bpb2RfZ2V0KCZjbGllbnQtPmRldiwgInNsZWVw IiwKPiArCQkJCQkgICAgICAgR1BJT0RfT1VUX0hJR0gpOwo+ICsJaWYgKElTX0VSUihwc19icmlk Z2UtPmdwaW9fc2xwX24pKSB7Cj4gKwkJcmV0ID0gUFRSX0VSUihwc19icmlkZ2UtPmdwaW9fc2xw X24pOwo+ICsJCWRldl9lcnIoZGV2LCAiY2Fubm90IGdldCBncGlvX3NscF9uOiAlZFxuIiwgcmV0 KTsKPiArCQlnb3RvIGV4aXQ7Cj4gKwl9Cj4gKwo+ICsJcHNfYnJpZGdlLT5ncGlvX3JzdF9uID0g ZGV2bV9ncGlvZF9nZXQoJmNsaWVudC0+ZGV2LCAicmVzZXQiLAo+ICsJCQkJCSAgICAgICBHUElP RF9PVVRfSElHSCk7Cj4gKwlpZiAoSVNfRVJSKHBzX2JyaWRnZS0+Z3Bpb19yc3RfbikpIHsKPiAr CQlyZXQgPSBQVFJfRVJSKHBzX2JyaWRnZS0+Z3Bpb19yc3Rfbik7Cj4gKwkJZGV2X2VycihkZXYs ICJjYW5ub3QgZ2V0IGdwaW9fcnN0X246ICVkXG4iLCByZXQpOwo+ICsJCWdvdG8gZXhpdDsKPiAr CX0KPiArCj4gKwlwc19icmlkZ2UtPmJyaWRnZS5mdW5jcyA9ICZwczg2NDBfYnJpZGdlX2Z1bmNz Owo+ICsJcHNfYnJpZGdlLT5icmlkZ2Uub2Zfbm9kZSA9IGRldi0+b2Zfbm9kZTsKPiArCXJldCA9 IGRybV9icmlkZ2VfYWRkKCZwc19icmlkZ2UtPmJyaWRnZSk7Cj4gKwlpZiAocmV0KSB7Cj4gKwkJ ZGV2X2VycihkZXYsICJGYWlsZWQgdG8gYWRkIGJyaWRnZTogJWRcbiIsIHJldCk7Cj4gKwkJZ290 byBleGl0Owo+ICsJfQo+ICsKPiArCXJldCA9IHBzODY0MF91cGRhdGVfZmlsZV9uYW1lKCZjbGll bnQtPmRldiwgJnBzX2JyaWRnZS0+ZndfZmlsZSwKPiArCQkJCSAgICAgIFBTX0ZXX05BTUUsIHN0 cmxlbihQU19GV19OQU1FKSk7Cj4gKwlpZiAocmV0KSB7Cj4gKwkJZGV2X2VycihkZXYsICJmYWls ZWQgdG8gdXBkYXRlIGZpbGUgbmFtZTogJWRcbiIsIHJldCk7Cj4gKwkJZ290byBleGl0Owo+ICsJ fQo+ICsKPiArCXBzX2JyaWRnZS0+cGFnZVswXSA9IGNsaWVudDsKPiArCj4gKwkvKiBwczg2NDAg dXNlcyBtdWx0aXBsZSBhZGRyZXNzZXMsIHVzZSBkdW1teSBkZXZpY2VzIGZvciB0aGVtCj4gKwkg ICogcGFnZVswXTogZm9yIERQIGNvbnRyb2wKPiArCSAgKiBwYWdlWzFdOiBmb3IgVklERU8gQnJp ZGdlCj4gKwkgICogcGFnZVsyXTogZm9yIGNvbnRyb2wgdG9wCj4gKwkgICogcGFnZVszXTogZm9y IERTSSBMaW5rIENvbnRyb2wxCj4gKwkgICogcGFnZVs0XTogZm9yIE1JUEkgUGh5Cj4gKwkgICog cGFnZVs1XTogZm9yIFZQTEwKPiArCSAgKiBwYWdlWzZdOiBmb3IgRFNJIExpbmsgQ29udHJvbDIK PiArCSAgKiBwYWdlWzddOiBmb3Igc3BpIHJvbSBtYXBwaW5nCj4gKwkqLwo+ICsJZm9yIChpID0g MTsgaSA8IDg7IGkrKykgewo+ICsJCXBzX2JyaWRnZS0+cGFnZVtpXSA9IGkyY19uZXdfZHVtbXko Y2xpZW50LT5hZGFwdGVyLAo+ICsJCQkJCQkgICBjbGllbnQtPmFkZHIgKyBpKTsKPiArCQlpZiAo IXBzX2JyaWRnZS0+cGFnZVtpXSkgewo+ICsJCQlkZXZfZXJyKGRldiwgImZhaWxlZCBpMmMgZHVt bXkgZGV2aWNlLCBhZGRyZXNzJTAyeFxuIiwKPiArCQkJCWNsaWVudC0+YWRkciArIGkpOwo+ICsJ CQlyZXQgPSAtRUJVU1k7Cj4gKwkJCWdvdG8gZXhpdF9kdW1teTsKPiArCQl9Cj4gKwl9Cj4gKwlp MmNfc2V0X2NsaWVudGRhdGEoY2xpZW50LCBwc19icmlkZ2UpOwo+ICsKPiArCXJldCA9IHN5c2Zz X2NyZWF0ZV9ncm91cCgmY2xpZW50LT5kZXYua29iaiwgJnBzODY0MF9hdHRyX2dyb3VwKTsKPiAr CWlmIChyZXQpIHsKPiArCQlkZXZfZXJyKGRldiwgImZhaWxlZCB0byBjcmVhdGUgc3lzZnMgZW50 cmllczogJWRcbiIsIHJldCk7Cj4gKwkJZ290byBleGl0X2R1bW15Owo+ICsJfQo+ICsKPiArCXJl dCA9IGRldm1fYWRkX2FjdGlvbihkZXYsIHBzODY0MF9yZW1vdmVfc3lzZnNfZ3JvdXAsIHBzX2Jy aWRnZSk7Cj4gKwlpZiAocmV0KSB7Cj4gKwkJcHM4NjQwX3JlbW92ZV9zeXNmc19ncm91cChwc19i cmlkZ2UpOwo+ICsJCWRldl9lcnIoZGV2LCAiZmFpbGVkIHRvIGFkZCBzeXNmcyBjbGVhbnVwIGFj dGlvbjogJWRcbiIsIHJldCk7Cj4gKwkJZ290byBleGl0X2R1bW15Owo+ICsJfQo+ICsJcmV0dXJu IDA7Cj4gKwo+ICtleGl0X2R1bW15Ogo+ICsJZm9yIChpID0gMTsgaSA8IDg7IGkrKykKPiArCQlp ZiAocHNfYnJpZGdlLT5wYWdlW2ldKQo+ICsJCQlpMmNfdW5yZWdpc3Rlcl9kZXZpY2UocHNfYnJp ZGdlLT5wYWdlW2ldKTsKPiArCj4gK2V4aXQ6Cj4gKwlyZXR1cm4gcmV0Owo+ICt9Cj4gKwo+ICtz dGF0aWMgaW50IHBzODY0MF9yZW1vdmUoc3RydWN0IGkyY19jbGllbnQgKmNsaWVudCkKPiArewo+ ICsJc3RydWN0IHBzODY0MCAqcHNfYnJpZGdlID0gaTJjX2dldF9jbGllbnRkYXRhKGNsaWVudCk7 Cj4gKwlpbnQgaTsKPiArCj4gKwlmb3IgKGkgPSAxOyBpIDwgODsgaSsrKQo+ICsJCWlmIChwc19i cmlkZ2UtPnBhZ2VbaV0pCj4gKwkJCWkyY191bnJlZ2lzdGVyX2RldmljZShwc19icmlkZ2UtPnBh Z2VbaV0pOwo+ICsKPiArCWRybV9icmlkZ2VfcmVtb3ZlKCZwc19icmlkZ2UtPmJyaWRnZSk7Cj4g Kwo+ICsJcmV0dXJuIDA7Cj4gK30KPiArCj4gK3N0YXRpYyBjb25zdCBzdHJ1Y3QgaTJjX2Rldmlj ZV9pZCBwczg2NDBfaTJjX3RhYmxlW10gPSB7Cj4gKwl7InBhcmFkZSxwczg2NDAiLCAwfSwKPiAr CXt9LAo+ICt9Owo+ICtNT0RVTEVfREVWSUNFX1RBQkxFKGkyYywgcHM4NjQwX2kyY190YWJsZSk7 Cj4gKwo+ICtzdGF0aWMgY29uc3Qgc3RydWN0IG9mX2RldmljZV9pZCBwczg2NDBfbWF0Y2hbXSA9 IHsKPiArCXsgLmNvbXBhdGlibGUgPSAicGFyYWRlLHBzODY0MCIgfSwKPiArCXt9LAo+ICt9Owo+ ICtNT0RVTEVfREVWSUNFX1RBQkxFKG9mLCBwczg2NDBfbWF0Y2gpOwo+ICsKPiArc3RhdGljIHN0 cnVjdCBpMmNfZHJpdmVyIHBzODY0MF9kcml2ZXIgPSB7Cj4gKwkuaWRfdGFibGUgPSBwczg2NDBf aTJjX3RhYmxlLAo+ICsJLnByb2JlID0gcHM4NjQwX3Byb2JlLAo+ICsJLnJlbW92ZSA9IHBzODY0 MF9yZW1vdmUsCj4gKwkuZHJpdmVyID0gewo+ICsJCS5uYW1lID0gInBhcmFkZSxwczg2NDAiLAo+ ICsJCS5vZl9tYXRjaF90YWJsZSA9IHBzODY0MF9tYXRjaCwKPiArCX0sCj4gK307Cj4gK21vZHVs ZV9pMmNfZHJpdmVyKHBzODY0MF9kcml2ZXIpOwo+ICsKPiArTU9EVUxFX0FVVEhPUigiSml0YW8g U2hpIDxqaXRhby5zaGlAbWVkaWF0ZWsuY29tPiIpOwo+ICtNT0RVTEVfQVVUSE9SKCJDSyBIdSA8 Y2suaHVAbWVkaWF0ZWsuY29tPiIpOwo+ICtNT0RVTEVfREVTQ1JJUFRJT04oIlBBUkFERSBwczg2 NDAgRFNJLWVEUCBjb252ZXJ0ZXIgZHJpdmVyIik7Cj4gK01PRFVMRV9MSUNFTlNFKCJHUEwgdjIi KTsKPgoKLS0gClRoZSBRdWFsY29tbSBJbm5vdmF0aW9uIENlbnRlciwgSW5jLiBpcyBhIG1lbWJl ciBvZiB0aGUgQ29kZSBBdXJvcmEgCkZvcnVtLCBob3N0ZWQgYnkgVGhlIExpbnV4IEZvdW5kYXRp b24KX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX18KZHJpLWRl dmVsIG1haWxpbmcgbGlzdApkcmktZGV2ZWxAbGlzdHMuZnJlZWRlc2t0b3Aub3JnCmh0dHA6Ly9s aXN0cy5mcmVlZGVza3RvcC5vcmcvbWFpbG1hbi9saXN0aW5mby9kcmktZGV2ZWwK From mboxrd@z Thu Jan 1 00:00:00 1970 From: architt@codeaurora.org (Archit Taneja) Date: Wed, 13 Jan 2016 12:41:10 +0530 Subject: [PATCH v7 2/2] drm/bridge: Add I2C based driver for ps8640 bridge In-Reply-To: <1452651494-16806-2-git-send-email-jitao.shi@mediatek.com> References: <1452651494-16806-1-git-send-email-jitao.shi@mediatek.com> <1452651494-16806-2-git-send-email-jitao.shi@mediatek.com> Message-ID: <5695F88E.9040808@codeaurora.org> To: linux-arm-kernel@lists.infradead.org List-Id: linux-arm-kernel.lists.infradead.org Hi Jitao, On 01/13/2016 07:48 AM, Jitao Shi wrote: > This patch adds drm_bridge driver for parade DSI to eDP bridge chip. > > Signed-off-by: Jitao Shi > --- > Changes since v6: > - Add ps8640 firmware update function > - Change i2c to i2c_transfer from i2c_master_recv/i2c_master_send > - Add comments for each page client I'd commented on the v6 patch set here: https://lkml.org/lkml/2015/12/15/62 I wanted to know how the bridge driver was transferring parameters to the dsi host. Is that something that you are hardcoding on both the dsi host and ps8640 drivers for now? My intention of asking this is to know if there are other users that can take advantage of this patch set: https://lkml.org/lkml/2015/12/10/283 Thanks, Archit > --- > drivers/gpu/drm/bridge/Kconfig | 10 + > drivers/gpu/drm/bridge/Makefile | 1 + > drivers/gpu/drm/bridge/parade-ps8640.c | 1106 ++++++++++++++++++++++++++++++++ > 3 files changed, 1117 insertions(+) > create mode 100644 drivers/gpu/drm/bridge/parade-ps8640.c > > diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig > index 6dddd39..dcfdbc9 100644 > --- a/drivers/gpu/drm/bridge/Kconfig > +++ b/drivers/gpu/drm/bridge/Kconfig > @@ -41,4 +41,14 @@ config DRM_PARADE_PS8622 > ---help--- > Parade eDP-LVDS bridge chip driver. > > +config DRM_PARADE_PS8640 > + tristate "Parade PS8640 MIPI DSI to eDP Converter" > + depends on OF > + select DRM_KMS_HELPER > + select DRM_PANEL > + ---help--- > + Choose this option if you have PS8640 for display > + The PS8640 is a high-performance and low-power > + MIPI DSI to eDP converter > + > endmenu > diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile > index d4e28be..272e3c01 100644 > --- a/drivers/gpu/drm/bridge/Makefile > +++ b/drivers/gpu/drm/bridge/Makefile > @@ -4,3 +4,4 @@ obj-$(CONFIG_DRM_DW_HDMI) += dw_hdmi.o > obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw_hdmi-ahb-audio.o > obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o > obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o > +obj-$(CONFIG_DRM_PARADE_PS8640) += parade-ps8640.o > diff --git a/drivers/gpu/drm/bridge/parade-ps8640.c b/drivers/gpu/drm/bridge/parade-ps8640.c > new file mode 100644 > index 0000000..a18d8e9 > --- /dev/null > +++ b/drivers/gpu/drm/bridge/parade-ps8640.c > @@ -0,0 +1,1106 @@ > +/* > + * Copyright (c) 2014 MediaTek Inc. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > +#include > +#include > + > +#define PAGE2_SPI_CFG3 0x82 > +#define I2C_TO_SPI_RESET 0x20 > +#define PAGE2_ROMADD_BYTE1 0x8e > +#define PAGE2_ROMADD_BYTE2 0x8f > +#define PAGE2_SWSPI_WDATA 0x90 > +#define PAGE2_SWSPI_RDATA 0x91 > +#define PAGE2_SWSPI_LEN 0x92 > +#define PAGE2_SWSPI_CTL 0x93 > +#define TRIGGER_NO_READBACK 0x05 > +#define TRIGGER_READBACK 0x01 > +#define PAGE2_SPI_STATUS 0x9e > +#define PAGE2_GPIO_L 0xa6 > +#define PAGE2_GPIO_H 0xa7 > +#define PS_GPIO9 BIT(1) > +#define PAGE2_IROM_CTRL 0xb0 > +#define IROM_ENABLE 0xc0 > +#define IROM_DISABLE 0x80 > +#define PAGE2_SW_REST 0xbc > +#define PAGE2_ENCTLSPI_WR 0xda > +#define PAGE2_I2C_BYPASS 0xea > +#define I2C_BYPASS_EN 0xd0 > + > +#define PAGE3_SET_ADD 0xfe > +#define PAGE3_SET_VAL 0xff > +#define VDO_CTL_ADD 0x13 > +#define VDO_DIS 0x18 > +#define VDO_EN 0x1c > + > +#define PAGE4_REV_L 0xf0 > +#define PAGE4_REV_H 0xf1 > +#define PAGE4_CHIP_L 0xf2 > +#define PAGE4_CHIP_H 0xf3 > + > +/* Firmware */ > +#define SPI_MAX_RETRY_CNT 8 > +#define PS_FW_NAME "ps864x_fw.bin" > + > +#define FW_CHIP_ID_OFFSET 0 > +#define FW_VERSION_OFFSET 2 > + > +#define bridge_to_ps8640(e) container_of(e, struct ps8640, bridge) > +#define connector_to_ps8640(e) container_of(e, struct ps8640, connector) > + > +struct ps8640_info { > + u8 family_id; > + u8 variant_id; > + u16 version; > +}; > + > +struct ps8640 { > + struct drm_connector connector; > + struct drm_bridge bridge; > + struct i2c_client *page[8]; > + struct ps8640_driver_data *driver_data; > + struct regulator *pwr_1v2_supply; > + struct regulator *pwr_3v3_supply; > + struct drm_panel *panel; > + struct gpio_desc *gpio_rst_n; > + struct gpio_desc *gpio_slp_n; > + struct gpio_desc *gpio_mode_sel_n; > + bool enabled; > + > + /* firmware file name */ > + bool in_fw_update; > + char *fw_file; > + struct ps8640_info info; > +}; > + > +static const u8 enc_ctrl_code[6] = {0xaa, 0x55, 0x50, 0x41, 0x52, 0x44}; > + > +static int ps8640_regr(struct i2c_client *client, u8 reg, u8 *data, > + u16 data_len) > +{ > + int ret; > + > + struct i2c_msg msgs[] = { > + { > + .addr = client->addr, > + .flags = 0, > + .len = 1, > + .buf = ®, > + }, > + { > + .addr = client->addr, > + .flags = I2C_M_RD, > + .len = data_len, > + .buf = data, > + } > + }; > + > + ret = i2c_transfer(client->adapter, msgs, 2); > + > + if (ret == 2) > + return 0; > + if (ret < 0) > + return ret; > + else > + return -EIO; > +} > + > +static int ps8640_regw(struct i2c_client *client, u8 reg, const u8 *data, > + u16 data_len) > +{ > + int ret, i; > + struct i2c_msg msg; > + u8 *buf; > + > + buf = devm_kzalloc(&client->dev, data_len + 1, GFP_KERNEL); > + /* i2c page size is 256 bytes, so limit the data_len 256 */ > + if (data_len > 256) { > + dev_err(&client->dev, "data_len must under 256: len = %d\n", > + data_len); > + return -EIO; > + } > + for (i = 0; i < data_len + 1; i++) > + buf[i] = (i == 0) ? reg : data[i - 1]; > + > + msg.addr = client->addr; > + msg.flags = 0; > + msg.len = data_len + 1; > + msg.buf = buf; > + > + ret = i2c_transfer(client->adapter, &msg, 1); > + if (ret == 1) > + return 0; > + if (ret < 0) > + return ret; > + else > + return -EIO; > +} > + > +static int ps8640_regw_byte(struct i2c_client *client, u8 reg, u8 data) > +{ > + int ret; > + struct i2c_msg msg; > + u8 buf[] = {reg, data}; > + > + msg.addr = client->addr; > + msg.flags = 0; > + msg.len = sizeof(buf); > + msg.buf = buf; > + > + ret = i2c_transfer(client->adapter, &msg, 1); > + if (ret == 1) > + return 0; > + if (ret < 0) > + return ret; > + else > + return -EIO; > +} > + > +static int ps8640_check_valid_id(struct ps8640 *ps_bridge) > +{ > + struct i2c_client *client = ps_bridge->page[4]; > + u8 chip_id[4]; > + > + ps8640_regr(client, PAGE4_REV_L, chip_id, 4); > + > + if ((chip_id[0] == 0x00) && (chip_id[1] == 0x0a) && > + (chip_id[2] == 0x00) && (chip_id[3] == 0x30)) > + return 0; > + > + return -ENODEV; > +} > + > +static void ps8640_show_mcu_fw_version(struct ps8640 *ps_bridge) > +{ > + struct i2c_client *client = ps_bridge->page[5]; > + u8 fw_ver[2]; > + > + ps8640_regr(client, 0x4, fw_ver, 2); > + ps_bridge->info.version = (fw_ver[0] << 8) | fw_ver[1]; > + > + DRM_INFO_ONCE("ps8640 rom fw version %d.%d\n", fw_ver[0], fw_ver[1]); > +} > + > +static int ps8640_bdg_enable(struct ps8640 *ps_bridge) > +{ > + struct i2c_client *client = ps_bridge->page[3]; > + int ret; > + u8 vdo_ctrl[2] = {VDO_CTL_ADD, VDO_EN}; > + > + ret = ps8640_check_valid_id(ps_bridge); > + if (ret) { > + DRM_ERROR("ps8640 not valid: %d\n", ret); > + return ret; > + } > + > + ret = ps8640_regw(client, PAGE3_SET_ADD, vdo_ctrl, 2); > + return ret; > +} > + > +static bool ps8640_in_bootloader(struct ps8640 *ps_bridge) > +{ > + return ps_bridge->in_fw_update; > +} > + > +static void ps8640_prepare(struct ps8640 *ps_bridge) > +{ > + struct i2c_client *client = ps_bridge->page[2]; > + int err, retry_cnt = 0; > + u8 set_vdo_done; > + > + if (ps8640_in_bootloader(ps_bridge)) > + return; > + > + if (ps_bridge->enabled) > + return; > + > + err = drm_panel_prepare(ps_bridge->panel); > + if (err < 0) { > + DRM_ERROR("failed to prepare panel: %d\n", err); > + return; > + } > + > + /* delay for power stable */ > + usleep_range(500, 700); > + > + err = regulator_enable(ps_bridge->pwr_1v2_supply); > + if (err < 0) { > + DRM_ERROR("failed to enable vdd12-supply: %d\n", err); > + goto err_panel_unprepare; > + } > + > + err = regulator_enable(ps_bridge->pwr_3v3_supply); > + if (err < 0) { > + DRM_ERROR("failed to enable vdd33-supply: %d\n", err); > + goto err_regulator_1v2_disable; > + } > + > + gpiod_set_value(ps_bridge->gpio_slp_n, 1); > + gpiod_set_value(ps_bridge->gpio_rst_n, 0); > + usleep_range(500, 700); > + gpiod_set_value(ps_bridge->gpio_rst_n, 1); > + > + /* wait for the ps8640 embed mcu ready > + * first wait 200ms and then check the mcu ready flag every 20ms > + */ > + msleep(200); > + do { > + err = ps8640_regr(client, PAGE2_GPIO_H, &set_vdo_done, 1); > + if (err < 0) { > + DRM_ERROR("failed read PAGE2_GPIO_H: %d\n", err); > + goto err_regulator_1v2_disable; > + } > + msleep(20); > + } while ((retry_cnt++ < 10) && ((set_vdo_done & PS_GPIO9) != PS_GPIO9)); > + > + ps8640_show_mcu_fw_version(ps_bridge); > + ps8640_regw_byte(client, PAGE2_I2C_BYPASS, I2C_BYPASS_EN); > + ps_bridge->enabled = true; > + > + return; > + > +err_regulator_1v2_disable: > + regulator_disable(ps_bridge->pwr_1v2_supply); > +err_panel_unprepare: > + drm_panel_unprepare(ps_bridge->panel); > +} > + > +static void ps8640_pre_enable(struct drm_bridge *bridge) > +{ > + struct ps8640 *ps_bridge = bridge_to_ps8640(bridge); > + > + ps8640_prepare(ps_bridge); > +} > + > +static void ps8640_enable(struct drm_bridge *bridge) > +{ > + struct ps8640 *ps_bridge = bridge_to_ps8640(bridge); > + int err; > + > + err = ps8640_bdg_enable(ps_bridge); > + if (err) > + DRM_ERROR("failed to enable unmutevideo: %d\n", err); > + > + err = drm_panel_enable(ps_bridge->panel); > + if (err < 0) > + DRM_ERROR("failed to enable panel: %d\n", err); > +} > + > +static void ps8640_disable(struct drm_bridge *bridge) > +{ > + struct ps8640 *ps_bridge = bridge_to_ps8640(bridge); > + int err; > + > + if (ps8640_in_bootloader(ps_bridge)) > + return; > + > + if (!ps_bridge->enabled) > + return; > + > + ps_bridge->enabled = false; > + > + err = drm_panel_disable(ps_bridge->panel); > + if (err < 0) > + DRM_ERROR("failed to disable panel: %d\n", err); > + > + gpiod_set_value(ps_bridge->gpio_rst_n, 0); > + gpiod_set_value(ps_bridge->gpio_slp_n, 0); > + regulator_disable(ps_bridge->pwr_3v3_supply); > + regulator_disable(ps_bridge->pwr_1v2_supply); > +} > + > +static void ps8640_post_disable(struct drm_bridge *bridge) > +{ > + struct ps8640 *ps_bridge = bridge_to_ps8640(bridge); > + int err; > + > + err = drm_panel_unprepare(ps_bridge->panel); > + if (err) > + DRM_ERROR("failed to unprepare panel: %d\n", err); > +} > + > +static int ps8640_get_modes(struct drm_connector *connector) > +{ > + struct ps8640 *ps_bridge = connector_to_ps8640(connector); > + > + ps8640_prepare(ps_bridge); > + > + return drm_panel_get_modes(ps_bridge->panel); > +} > + > +static struct drm_encoder *ps8640_best_encoder(struct drm_connector *connector) > +{ > + struct ps8640 *ps_bridge; > + > + ps_bridge = connector_to_ps8640(connector); > + return ps_bridge->bridge.encoder; > +} > + > +static const struct drm_connector_helper_funcs ps8640_connector_helper_funcs = { > + .get_modes = ps8640_get_modes, > + .best_encoder = ps8640_best_encoder, > +}; > + > +static enum drm_connector_status ps8640_detect(struct drm_connector *connector, > + bool force) > +{ > + return connector_status_connected; > +} > + > +static void ps8640_connector_destroy(struct drm_connector *connector) > +{ > + drm_connector_unregister(connector); > + drm_connector_cleanup(connector); > +} > + > +static const struct drm_connector_funcs ps8640_connector_funcs = { > + .dpms = drm_atomic_helper_connector_dpms, > + .fill_modes = drm_helper_probe_single_connector_modes, > + .detect = ps8640_detect, > + .destroy = ps8640_connector_destroy, > + .reset = drm_atomic_helper_connector_reset, > + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, > + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, > +}; > + > +int ps8640_bridge_attach(struct drm_bridge *bridge) > +{ > + struct ps8640 *ps_bridge = bridge_to_ps8640(bridge); > + int ret; > + > + if (!bridge->encoder) { > + DRM_ERROR("Parent encoder object not found"); > + return -ENODEV; > + } > + > + ret = drm_connector_init(bridge->dev, &ps_bridge->connector, > + &ps8640_connector_funcs, > + DRM_MODE_CONNECTOR_eDP); > + > + if (ret) { > + DRM_ERROR("Failed to initialize connector with drm: %d\n", ret); > + return ret; > + } > + > + drm_connector_helper_add(&ps_bridge->connector, > + &ps8640_connector_helper_funcs); > + drm_connector_register(&ps_bridge->connector); > + > + ps_bridge->connector.dpms = DRM_MODE_DPMS_ON; > + drm_mode_connector_attach_encoder(&ps_bridge->connector, > + bridge->encoder); > + > + if (ps_bridge->panel) > + drm_panel_attach(ps_bridge->panel, &ps_bridge->connector); > + > + return ret; > +} > + > +static bool ps8640_bridge_mode_fixup(struct drm_bridge *bridge, > + const struct drm_display_mode *mode, > + struct drm_display_mode *adjusted_mode) > +{ > + return true; > +} > + > +static const struct drm_bridge_funcs ps8640_bridge_funcs = { > + .attach = ps8640_bridge_attach, > + .mode_fixup = ps8640_bridge_mode_fixup, > + .disable = ps8640_disable, > + .post_disable = ps8640_post_disable, > + .pre_enable = ps8640_pre_enable, > + .enable = ps8640_enable, > +}; > + > +static ssize_t ps8640_fw_file_show(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + struct ps8640 *ps_bridge = dev_get_drvdata(dev); > + > + return scnprintf(buf, PAGE_SIZE, "%s\n", ps_bridge->fw_file); > +} > + > +static int ps8640_update_file_name(struct device *dev, char **file_name, > + const char *buf, size_t count) > +{ > + char *new_file_name; > + > + /* Simple sanity check */ > + if (count > 64) { > + dev_warn(dev, "File name too long\n"); > + return -EINVAL; > + } > + > + new_file_name = devm_kmalloc(dev, count + 1, GFP_KERNEL); > + if (!new_file_name) > + return -ENOMEM; > + > + memcpy(new_file_name, buf, count + 1); > + > + /* Echo into the sysfs entry may append newline at the end of buf */ > + if (new_file_name[count - 1] == '\n') > + count--; > + > + new_file_name[count] = '\0'; > + > + if (*file_name) > + devm_kfree(dev, *file_name); > + > + *file_name = new_file_name; > + > + return 0; > +} > + > +static ssize_t ps8640_fw_file_store(struct device *dev, > + struct device_attribute *attr, > + const char *buf, size_t count) > +{ > + struct ps8640 *ps_bridge = dev_get_drvdata(dev); > + int ret; > + > + ret = ps8640_update_file_name(dev, &ps_bridge->fw_file, buf, count); > + if (ret) > + return ret; > + > + return count; > +} > + > +/* Firmware Version is returned as Major.Minor.Build */ > +static ssize_t ps8640_fw_version_show(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + struct ps8640 *ps_bridge = dev_get_drvdata(dev); > + struct ps8640_info *info = &ps_bridge->info; > + > + return scnprintf(buf, PAGE_SIZE, "%u.%u\n", info->version >> 8, > + info->version & 0xff); > +} > + > +/* Hardware Version is returned as FamilyID.VariantID */ > +static ssize_t ps8640_hw_version_show(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + struct ps8640 *ps_bridge = dev_get_drvdata(dev); > + struct ps8640_info *info = &ps_bridge->info; > + > + return scnprintf(buf, PAGE_SIZE, "ps%u.%u\n", info->family_id, > + info->variant_id); > +} > + > +static int ps8640_spi_send_cmd(struct ps8640 *ps_bridge, u8 *cmd, u8 cmd_len) > +{ > + struct i2c_client *client = ps_bridge->page[2]; > + u8 i; > + int ret; > + > + ret = ps8640_regw_byte(client, PAGE2_IROM_CTRL, IROM_ENABLE); > + if (ret) > + goto err; > + > + /* write command in write port */ > + for (i = 0; i < cmd_len; i++) { > + ret = ps8640_regw_byte(client, PAGE2_SWSPI_WDATA, cmd[i]); > + if (ret) > + goto err; > + } > + /* command length */ > + ret = ps8640_regw_byte(client, PAGE2_SWSPI_LEN, cmd_len - 1); > + if (ret) > + goto err; > + /* trigger read */ > + ret = ps8640_regw_byte(client, PAGE2_SWSPI_CTL, TRIGGER_NO_READBACK); > + if (ret) > + goto err; > + > + ret = ps8640_regw_byte(client, PAGE2_IROM_CTRL, IROM_DISABLE); > + if (ret) > + goto err; > + > + return 0; > + > +err: > + dev_err(&client->dev, "send command err: %d\n", ret); > + return ret; > +} > + > +static int ps8640_wait_spi_ready(struct ps8640 *ps_bridge) > +{ > + struct i2c_client *client = ps_bridge->page[2]; > + u8 spi_rdy_st, retry_cnt = 0; > + > + do { > + ps8640_regr(client, PAGE2_SPI_STATUS, &spi_rdy_st, 1); > + msleep(20); > + if ((retry_cnt == SPI_MAX_RETRY_CNT) && > + ((spi_rdy_st & 0x0c) != 0x0c)) { > + dev_err(&client->dev, "wait spi ready timeout\n"); > + return -EBUSY; > + } > + } while ((retry_cnt++ < SPI_MAX_RETRY_CNT) && > + (spi_rdy_st & 0x0c) == 0x0c); > + > + return 0; > +} > + > +static int ps8640_wait_spi_nobusy(struct ps8640 *ps_bridge) > +{ > + struct i2c_client *client = ps_bridge->page[2]; > + u8 spi_status, retry_cnt = 0; > + int ret; > + > + do { > + /* 0x05 RDSR; Read-Status-Register */ > + ret = ps8640_regw_byte(client, PAGE2_SWSPI_WDATA, 0x05); > + if (ret) > + goto err_send_cmd_exit; > + > + /* command length */ > + ret = ps8640_regw_byte(client, PAGE2_SWSPI_LEN, 0x00); > + if (ret) > + goto err_send_cmd_exit; > + > + /* trigger read */ > + ret = ps8640_regw_byte(client, PAGE2_SWSPI_CTL, > + TRIGGER_READBACK); > + if (ret) > + goto err_send_cmd_exit; > + > + /* delay for cmd send */ > + usleep_range(100, 300); > + /* wait for SPI ROM until not busy */ > + ret = ps8640_regr(client, PAGE2_SWSPI_RDATA, &spi_status, 1); > + if (ret) > + goto err_send_cmd_exit; > + } while ((retry_cnt++ < SPI_MAX_RETRY_CNT) && > + (spi_status & 0x0c) == 0x0c); > + > + if ((retry_cnt > SPI_MAX_RETRY_CNT) && (spi_status & 0x0c) != 0x0c) { > + ret = -EBUSY; > + dev_err(&client->dev, "wait spi no busy timeout: %d\n", ret); > + goto err_timeout; > + } > + > + return 0; > + > +err_send_cmd_exit: > + dev_err(&client->dev, "send command err: %d\n", ret); > + > +err_timeout: > + return ret; > +} > + > +static int ps8640_wait_rom_idle(struct ps8640 *ps_bridge) > +{ > + struct i2c_client *client = ps_bridge->page[0]; > + int ret; > + > + ret = ps8640_regw_byte(client, PAGE2_IROM_CTRL, IROM_ENABLE); > + if (ret) > + goto exit; > + > + ret = ps8640_wait_spi_ready(ps_bridge); > + if (ret) > + goto exit; > + > + ret = ps8640_wait_spi_nobusy(ps_bridge); > + if (ret) > + goto exit; > + > + ret = ps8640_regw_byte(client, PAGE2_IROM_CTRL, IROM_DISABLE); > + if (ret) > + goto exit; > + > +exit: > + if (ret) > + dev_err(&client->dev, "wait ps8640 rom idle fail: %d\n", ret); > + > + return ret; > +} > + > +static int ps8640_spi_dl_mode(struct ps8640 *ps_bridge) > +{ > + struct i2c_client *client = ps_bridge->page[2]; > + int ret; > + > + /* switch ps8640 mode to spi dl mode */ > + gpiod_set_value(ps_bridge->gpio_mode_sel_n, 0); > + > + /* reset spi interface */ > + ret = ps8640_regw_byte(client, PAGE2_SW_REST, 0xc0); > + if (ret) > + goto exit; > + > + ret = ps8640_regw_byte(client, PAGE2_SW_REST, 0x40); > + if (ret) > + goto exit; > + > +exit: > + if (ret) > + dev_err(&client->dev, "fail reset spi interface: %d\n", ret); > + > + return ret; > +} > + > +static int ps8640_rom_prepare(struct ps8640 *ps_bridge) > +{ > + struct i2c_client *client = ps_bridge->page[2]; > + struct device *dev = &client->dev; > + u8 i, cmd[2]; > + int ret; > + > + /* Enable-Write-Status-Register */ > + cmd[0] = 0x06; > + ret = ps8640_spi_send_cmd(ps_bridge, cmd, 1); > + if (ret) { > + dev_err(dev, "failed enable-write-status-register: %d\n", ret); > + return ret; > + } > + > + /* disable all protection */ > + cmd[0] = 0x01; > + cmd[1] = 0x00; > + ret = ps8640_spi_send_cmd(ps_bridge, cmd, 2); > + if (ret) { > + dev_err(dev, "fail disable all protection: %d\n", ret); > + return ret; > + } > + > + /* wait for SPI module ready */ > + ret = ps8640_wait_rom_idle(ps_bridge); > + if (ret) { > + dev_err(dev, "fail wait rom idle: %d\n", ret); > + return ret; > + } > + > + ps8640_regw_byte(client, PAGE2_IROM_CTRL, IROM_ENABLE); > + for (i = 0; i < 6; i++) > + ps8640_regw_byte(client, PAGE2_ENCTLSPI_WR, enc_ctrl_code[i]); > + ps8640_regw_byte(client, PAGE2_IROM_CTRL, IROM_DISABLE); > + > + /* Enable-Write-Status-Register */ > + cmd[0] = 0x06; > + ret = ps8640_spi_send_cmd(ps_bridge, cmd, 1); > + if (ret) { > + dev_err(dev, "fail enable-write-status-register: %d\n", ret); > + return ret; > + } > + > + /* chip erase command */ > + cmd[0] = 0xc7; > + ret = ps8640_spi_send_cmd(ps_bridge, cmd, 1); > + if (ret) { > + dev_err(dev, "fail disable all protection: %d\n", ret); > + return ret; > + } > + > + ret = ps8640_wait_rom_idle(ps_bridge); > + if (ret) { > + dev_err(dev, "fail wait rom idle: %d\n", ret); > + return ret; > + } > + > + return 0; > +} > + > +static int ps8640_validate_firmware(struct ps8640 *ps_bridge, > + const struct firmware *fw) > +{ > + struct i2c_client *client = ps_bridge->page[0]; > + struct ps8640_info *info = &ps_bridge->info; > + u16 fw_chip_id, fw_version_id; > + > + /* > + * Get the chip_id from the firmware. Make sure that it is the > + * right controller to do the firmware and config update. > + */ > + fw_chip_id = get_unaligned_le16(fw->data + FW_CHIP_ID_OFFSET); > + > + if (fw_chip_id != 0x8640) { > + dev_err(&client->dev, > + "chip id mismatch: fw 0x%x vs. chip 0x8640\n", > + fw_chip_id); > + return -ENODEV; > + } > + > + fw_version_id = get_unaligned_le16(fw->data + FW_VERSION_OFFSET); > + > + if (fw_chip_id != info->version) { > + dev_err(&client->dev, > + "fw version mismatch: fw %d.%d vs. chip %d.%d\n", > + fw_version_id >> 8, fw_version_id & 0xff, > + info->version >> 8, info->version & 0xff); > + return -ENODEV; > + } > + > + return 0; > +} > + > +static int ps8640_write_rom(struct ps8640 *ps_bridge, const struct firmware *fw) > +{ > + struct i2c_client *client = ps_bridge->page[0]; > + struct device *dev = &client->dev; > + struct i2c_client *client2 = ps_bridge->page[2]; > + struct i2c_client *client7 = ps_bridge->page[7]; > + unsigned int pos = 0; > + u8 progress_cnt, rom_page_id[2]; > + int ret; > + > + ps8640_regw_byte(client2, PAGE2_SPI_CFG3, I2C_TO_SPI_RESET); > + msleep(100); > + ps8640_regw_byte(client2, PAGE2_SPI_CFG3, 0x00); > + > + while (pos < fw->size) { > + rom_page_id[0] = (pos >> 8) & 0xFF; > + rom_page_id[1] = (pos >> 16) & 0xFF; > + ret = ps8640_regw(client2, PAGE2_ROMADD_BYTE1, rom_page_id, 2); > + if (ret) > + goto error; > + ret = ps8640_regw(client7, 0, fw->data + pos, 256); > + if (ret) > + goto error; > + > + pos += 256; > + if (progress_cnt != (pos * 100) / fw->size) { > + progress_cnt = (pos * 100) / fw->size; > + dev_info(dev, "fw update progress percent %d\n", > + progress_cnt); > + } > + } > + return 0; > + > +error: > + dev_err(dev, "failed write extenal flash, %d\n", ret); > + return ret; > +} > + > +static int ps8640_spi_normal_mode(struct ps8640 *ps_bridge) > +{ > + u8 cmd[2]; > + struct i2c_client *client = ps_bridge->page[2]; > + > + /* Enable-Write-Status-Register */ > + cmd[0] = 0x06; > + ps8640_spi_send_cmd(ps_bridge, cmd, 1); > + > + /* protect BPL/BP0/BP1 */ > + cmd[0] = 0x01; > + cmd[1] = 0x8c; > + ps8640_spi_send_cmd(ps_bridge, cmd, 2); > + > + /* wait for SPI rom ready */ > + ps8640_wait_rom_idle(ps_bridge); > + > + /* disable PS8640 mapping function */ > + ps8640_regw_byte(client, PAGE2_ENCTLSPI_WR, 0x00); > + > + gpiod_set_value(ps_bridge->gpio_mode_sel_n, 1); > + return 0; > +} > + > +static int ps8640_enter_bl(struct ps8640 *ps_bridge) > +{ > + int ret; > + > + ret = ps8640_spi_dl_mode(ps_bridge); > + if (ret) > + return ret; > + > + ps_bridge->in_fw_update = true; > + return 0; > +} > + > +static void ps8640_exit_bl(struct ps8640 *ps_bridge, const struct firmware *fw) > +{ > + ps_bridge->in_fw_update = false; > + ps8640_spi_normal_mode(ps_bridge); > +} > + > +static int ps8640_load_fw(struct ps8640 *ps_bridge, const struct firmware *fw) > +{ > + struct i2c_client *client = ps_bridge->page[0]; > + struct device *dev = &client->dev; > + int ret; > + bool ps8640_status_backup = ps_bridge->enabled; > + > + if (!ps8640_in_bootloader(ps_bridge)) { > + if (!ps8640_status_backup) > + ps8640_prepare(ps_bridge); > + > + ret = ps8640_enter_bl(ps_bridge); > + if (ret) > + goto exit; > + } > + > + ret = ps8640_validate_firmware(ps_bridge, fw); > + if (ret) > + goto exit; > + > + ret = ps8640_rom_prepare(ps_bridge); > + if (ret) > + goto exit; > + > + ret = ps8640_write_rom(ps_bridge, fw); > + > +exit: > + if (ret) > + dev_err(dev, "Failed to load firmware, %d\n", ret); > + > + ps8640_exit_bl(ps_bridge, fw); > + if (!ps8640_status_backup) > + ps8640_disable(&ps_bridge->bridge); > + return ret; > +} > + > +static ssize_t ps8640_update_fw_store(struct device *dev, > + struct device_attribute *attr, > + const char *buf, size_t count) > +{ > + struct i2c_client *client = to_i2c_client(dev); > + struct ps8640 *ps_bridge = i2c_get_clientdata(client); > + const struct firmware *fw; > + int error; > + > + error = request_firmware(&fw, ps_bridge->fw_file, dev); > + if (error) { > + dev_err(dev, "Unable to open firmware %s: %d\n", > + ps_bridge->fw_file, error); > + return error; > + } > + > + error = ps8640_load_fw(ps_bridge, fw); > + if (error) > + dev_err(dev, "The firmware update failed(%d)\n", error); > + else > + dev_info(dev, "The firmware update succeeded\n"); > + > + release_firmware(fw); > + return error ? error : count; > +} > + > +static DEVICE_ATTR(fw_file, S_IRUGO | S_IWUSR, ps8640_fw_file_show, > + ps8640_fw_file_store); > +static DEVICE_ATTR(fw_version, S_IRUGO, ps8640_fw_version_show, NULL); > +static DEVICE_ATTR(hw_version, S_IRUGO, ps8640_hw_version_show, NULL); > +static DEVICE_ATTR(update_fw, S_IWUSR, NULL, ps8640_update_fw_store); > + > +static struct attribute *ps8640_attrs[] = { > + &dev_attr_fw_file.attr, > + &dev_attr_fw_version.attr, > + &dev_attr_hw_version.attr, > + &dev_attr_update_fw.attr, > + NULL > +}; > + > +static const struct attribute_group ps8640_attr_group = { > + .attrs = ps8640_attrs, > +}; > + > +static void ps8640_remove_sysfs_group(void *data) > +{ > + struct ps8640 *ps_bridge = data; > + > + sysfs_remove_group(&ps_bridge->page[0]->dev.kobj, &ps8640_attr_group); > +} > + > +/* for fireware update end */ > + > +static int ps8640_probe(struct i2c_client *client, > + const struct i2c_device_id *id) > +{ > + struct device *dev = &client->dev; > + struct ps8640 *ps_bridge; > + struct device_node *np = dev->of_node; > + struct device_node *port, *out_ep; > + struct device_node *panel_node = NULL; > + int i, ret; > + > + ps_bridge = devm_kzalloc(dev, sizeof(*ps_bridge), GFP_KERNEL); > + if (!ps_bridge) { > + ret = -ENOMEM; > + goto exit; > + } > + /* port at 1 is ps8640 output port */ > + port = of_graph_get_port_by_id(np, 1); > + if (port) { > + out_ep = of_get_child_by_name(port, "endpoint"); > + of_node_put(port); > + if (out_ep) { > + panel_node = of_graph_get_remote_port_parent(out_ep); > + of_node_put(out_ep); > + } > + } > + if (panel_node) { > + ps_bridge->panel = of_drm_find_panel(panel_node); > + of_node_put(panel_node); > + if (!ps_bridge->panel) { > + ret = -EPROBE_DEFER; > + goto exit; > + } > + } > + > + ps_bridge->pwr_3v3_supply = devm_regulator_get(dev, "vdd33"); > + if (IS_ERR(ps_bridge->pwr_3v3_supply)) { > + ret = PTR_ERR(ps_bridge->pwr_3v3_supply); > + dev_err(dev, "cannot get vdd33 supply: %d\n", ret); > + goto exit; > + } > + > + ps_bridge->pwr_1v2_supply = devm_regulator_get(dev, "vdd12"); > + if (IS_ERR(ps_bridge->pwr_1v2_supply)) { > + ret = PTR_ERR(ps_bridge->pwr_1v2_supply); > + dev_err(dev, "cannot get vdd12 supply: %d\n", ret); > + goto exit; > + } > + > + ps_bridge->gpio_mode_sel_n = devm_gpiod_get(&client->dev, "mode-sel", > + GPIOD_OUT_HIGH); > + if (IS_ERR(ps_bridge->gpio_mode_sel_n)) { > + ret = PTR_ERR(ps_bridge->gpio_mode_sel_n); > + dev_err(dev, "cannot get gpio_mode_sel_n %d\n", ret); > + goto exit; > + } > + > + ps_bridge->gpio_slp_n = devm_gpiod_get(&client->dev, "sleep", > + GPIOD_OUT_HIGH); > + if (IS_ERR(ps_bridge->gpio_slp_n)) { > + ret = PTR_ERR(ps_bridge->gpio_slp_n); > + dev_err(dev, "cannot get gpio_slp_n: %d\n", ret); > + goto exit; > + } > + > + ps_bridge->gpio_rst_n = devm_gpiod_get(&client->dev, "reset", > + GPIOD_OUT_HIGH); > + if (IS_ERR(ps_bridge->gpio_rst_n)) { > + ret = PTR_ERR(ps_bridge->gpio_rst_n); > + dev_err(dev, "cannot get gpio_rst_n: %d\n", ret); > + goto exit; > + } > + > + ps_bridge->bridge.funcs = &ps8640_bridge_funcs; > + ps_bridge->bridge.of_node = dev->of_node; > + ret = drm_bridge_add(&ps_bridge->bridge); > + if (ret) { > + dev_err(dev, "Failed to add bridge: %d\n", ret); > + goto exit; > + } > + > + ret = ps8640_update_file_name(&client->dev, &ps_bridge->fw_file, > + PS_FW_NAME, strlen(PS_FW_NAME)); > + if (ret) { > + dev_err(dev, "failed to update file name: %d\n", ret); > + goto exit; > + } > + > + ps_bridge->page[0] = client; > + > + /* ps8640 uses multiple addresses, use dummy devices for them > + * page[0]: for DP control > + * page[1]: for VIDEO Bridge > + * page[2]: for control top > + * page[3]: for DSI Link Control1 > + * page[4]: for MIPI Phy > + * page[5]: for VPLL > + * page[6]: for DSI Link Control2 > + * page[7]: for spi rom mapping > + */ > + for (i = 1; i < 8; i++) { > + ps_bridge->page[i] = i2c_new_dummy(client->adapter, > + client->addr + i); > + if (!ps_bridge->page[i]) { > + dev_err(dev, "failed i2c dummy device, address%02x\n", > + client->addr + i); > + ret = -EBUSY; > + goto exit_dummy; > + } > + } > + i2c_set_clientdata(client, ps_bridge); > + > + ret = sysfs_create_group(&client->dev.kobj, &ps8640_attr_group); > + if (ret) { > + dev_err(dev, "failed to create sysfs entries: %d\n", ret); > + goto exit_dummy; > + } > + > + ret = devm_add_action(dev, ps8640_remove_sysfs_group, ps_bridge); > + if (ret) { > + ps8640_remove_sysfs_group(ps_bridge); > + dev_err(dev, "failed to add sysfs cleanup action: %d\n", ret); > + goto exit_dummy; > + } > + return 0; > + > +exit_dummy: > + for (i = 1; i < 8; i++) > + if (ps_bridge->page[i]) > + i2c_unregister_device(ps_bridge->page[i]); > + > +exit: > + return ret; > +} > + > +static int ps8640_remove(struct i2c_client *client) > +{ > + struct ps8640 *ps_bridge = i2c_get_clientdata(client); > + int i; > + > + for (i = 1; i < 8; i++) > + if (ps_bridge->page[i]) > + i2c_unregister_device(ps_bridge->page[i]); > + > + drm_bridge_remove(&ps_bridge->bridge); > + > + return 0; > +} > + > +static const struct i2c_device_id ps8640_i2c_table[] = { > + {"parade,ps8640", 0}, > + {}, > +}; > +MODULE_DEVICE_TABLE(i2c, ps8640_i2c_table); > + > +static const struct of_device_id ps8640_match[] = { > + { .compatible = "parade,ps8640" }, > + {}, > +}; > +MODULE_DEVICE_TABLE(of, ps8640_match); > + > +static struct i2c_driver ps8640_driver = { > + .id_table = ps8640_i2c_table, > + .probe = ps8640_probe, > + .remove = ps8640_remove, > + .driver = { > + .name = "parade,ps8640", > + .of_match_table = ps8640_match, > + }, > +}; > +module_i2c_driver(ps8640_driver); > + > +MODULE_AUTHOR("Jitao Shi "); > +MODULE_AUTHOR("CK Hu "); > +MODULE_DESCRIPTION("PARADE ps8640 DSI-eDP converter driver"); > +MODULE_LICENSE("GPL v2"); > -- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, hosted by The Linux Foundation From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755362AbcAMHL2 (ORCPT ); Wed, 13 Jan 2016 02:11:28 -0500 Received: from smtp.codeaurora.org ([198.145.29.96]:42360 "EHLO smtp.codeaurora.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751255AbcAMHLZ (ORCPT ); Wed, 13 Jan 2016 02:11:25 -0500 Subject: Re: [PATCH v7 2/2] drm/bridge: Add I2C based driver for ps8640 bridge To: Jitao Shi References: <1452651494-16806-1-git-send-email-jitao.shi@mediatek.com> <1452651494-16806-2-git-send-email-jitao.shi@mediatek.com> Cc: Rob Herring , Pawel Moll , Mark Rutland , Ian Campbell , Kumar Gala , David Airlie , Matthias Brugger , stonea168@163.com, dri-devel@lists.freedesktop.org, Andy Yan , Ajay Kumar , Vincent Palatin , cawa.cheng@mediatek.com, Russell King , Thierry Reding , devicetree@vger.kernel.org, linux-mediatek@lists.infradead.org, yingjoe.chen@mediatek.com, eddie.huang@mediatek.com, linux-arm-kernel@lists.infradead.org, Rahul Sharma , srv_heupstream@mediatek.com, linux-kernel@vger.kernel.org, Sascha Hauer From: Archit Taneja Message-ID: <5695F88E.9040808@codeaurora.org> Date: Wed, 13 Jan 2016 12:41:10 +0530 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Thunderbird/38.2.0 MIME-Version: 1.0 In-Reply-To: <1452651494-16806-2-git-send-email-jitao.shi@mediatek.com> Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 7bit Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Hi Jitao, On 01/13/2016 07:48 AM, Jitao Shi wrote: > This patch adds drm_bridge driver for parade DSI to eDP bridge chip. > > Signed-off-by: Jitao Shi > --- > Changes since v6: > - Add ps8640 firmware update function > - Change i2c to i2c_transfer from i2c_master_recv/i2c_master_send > - Add comments for each page client I'd commented on the v6 patch set here: https://lkml.org/lkml/2015/12/15/62 I wanted to know how the bridge driver was transferring parameters to the dsi host. Is that something that you are hardcoding on both the dsi host and ps8640 drivers for now? My intention of asking this is to know if there are other users that can take advantage of this patch set: https://lkml.org/lkml/2015/12/10/283 Thanks, Archit > --- > drivers/gpu/drm/bridge/Kconfig | 10 + > drivers/gpu/drm/bridge/Makefile | 1 + > drivers/gpu/drm/bridge/parade-ps8640.c | 1106 ++++++++++++++++++++++++++++++++ > 3 files changed, 1117 insertions(+) > create mode 100644 drivers/gpu/drm/bridge/parade-ps8640.c > > diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig > index 6dddd39..dcfdbc9 100644 > --- a/drivers/gpu/drm/bridge/Kconfig > +++ b/drivers/gpu/drm/bridge/Kconfig > @@ -41,4 +41,14 @@ config DRM_PARADE_PS8622 > ---help--- > Parade eDP-LVDS bridge chip driver. > > +config DRM_PARADE_PS8640 > + tristate "Parade PS8640 MIPI DSI to eDP Converter" > + depends on OF > + select DRM_KMS_HELPER > + select DRM_PANEL > + ---help--- > + Choose this option if you have PS8640 for display > + The PS8640 is a high-performance and low-power > + MIPI DSI to eDP converter > + > endmenu > diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile > index d4e28be..272e3c01 100644 > --- a/drivers/gpu/drm/bridge/Makefile > +++ b/drivers/gpu/drm/bridge/Makefile > @@ -4,3 +4,4 @@ obj-$(CONFIG_DRM_DW_HDMI) += dw_hdmi.o > obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw_hdmi-ahb-audio.o > obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o > obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o > +obj-$(CONFIG_DRM_PARADE_PS8640) += parade-ps8640.o > diff --git a/drivers/gpu/drm/bridge/parade-ps8640.c b/drivers/gpu/drm/bridge/parade-ps8640.c > new file mode 100644 > index 0000000..a18d8e9 > --- /dev/null > +++ b/drivers/gpu/drm/bridge/parade-ps8640.c > @@ -0,0 +1,1106 @@ > +/* > + * Copyright (c) 2014 MediaTek Inc. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > +#include > +#include > + > +#define PAGE2_SPI_CFG3 0x82 > +#define I2C_TO_SPI_RESET 0x20 > +#define PAGE2_ROMADD_BYTE1 0x8e > +#define PAGE2_ROMADD_BYTE2 0x8f > +#define PAGE2_SWSPI_WDATA 0x90 > +#define PAGE2_SWSPI_RDATA 0x91 > +#define PAGE2_SWSPI_LEN 0x92 > +#define PAGE2_SWSPI_CTL 0x93 > +#define TRIGGER_NO_READBACK 0x05 > +#define TRIGGER_READBACK 0x01 > +#define PAGE2_SPI_STATUS 0x9e > +#define PAGE2_GPIO_L 0xa6 > +#define PAGE2_GPIO_H 0xa7 > +#define PS_GPIO9 BIT(1) > +#define PAGE2_IROM_CTRL 0xb0 > +#define IROM_ENABLE 0xc0 > +#define IROM_DISABLE 0x80 > +#define PAGE2_SW_REST 0xbc > +#define PAGE2_ENCTLSPI_WR 0xda > +#define PAGE2_I2C_BYPASS 0xea > +#define I2C_BYPASS_EN 0xd0 > + > +#define PAGE3_SET_ADD 0xfe > +#define PAGE3_SET_VAL 0xff > +#define VDO_CTL_ADD 0x13 > +#define VDO_DIS 0x18 > +#define VDO_EN 0x1c > + > +#define PAGE4_REV_L 0xf0 > +#define PAGE4_REV_H 0xf1 > +#define PAGE4_CHIP_L 0xf2 > +#define PAGE4_CHIP_H 0xf3 > + > +/* Firmware */ > +#define SPI_MAX_RETRY_CNT 8 > +#define PS_FW_NAME "ps864x_fw.bin" > + > +#define FW_CHIP_ID_OFFSET 0 > +#define FW_VERSION_OFFSET 2 > + > +#define bridge_to_ps8640(e) container_of(e, struct ps8640, bridge) > +#define connector_to_ps8640(e) container_of(e, struct ps8640, connector) > + > +struct ps8640_info { > + u8 family_id; > + u8 variant_id; > + u16 version; > +}; > + > +struct ps8640 { > + struct drm_connector connector; > + struct drm_bridge bridge; > + struct i2c_client *page[8]; > + struct ps8640_driver_data *driver_data; > + struct regulator *pwr_1v2_supply; > + struct regulator *pwr_3v3_supply; > + struct drm_panel *panel; > + struct gpio_desc *gpio_rst_n; > + struct gpio_desc *gpio_slp_n; > + struct gpio_desc *gpio_mode_sel_n; > + bool enabled; > + > + /* firmware file name */ > + bool in_fw_update; > + char *fw_file; > + struct ps8640_info info; > +}; > + > +static const u8 enc_ctrl_code[6] = {0xaa, 0x55, 0x50, 0x41, 0x52, 0x44}; > + > +static int ps8640_regr(struct i2c_client *client, u8 reg, u8 *data, > + u16 data_len) > +{ > + int ret; > + > + struct i2c_msg msgs[] = { > + { > + .addr = client->addr, > + .flags = 0, > + .len = 1, > + .buf = ®, > + }, > + { > + .addr = client->addr, > + .flags = I2C_M_RD, > + .len = data_len, > + .buf = data, > + } > + }; > + > + ret = i2c_transfer(client->adapter, msgs, 2); > + > + if (ret == 2) > + return 0; > + if (ret < 0) > + return ret; > + else > + return -EIO; > +} > + > +static int ps8640_regw(struct i2c_client *client, u8 reg, const u8 *data, > + u16 data_len) > +{ > + int ret, i; > + struct i2c_msg msg; > + u8 *buf; > + > + buf = devm_kzalloc(&client->dev, data_len + 1, GFP_KERNEL); > + /* i2c page size is 256 bytes, so limit the data_len 256 */ > + if (data_len > 256) { > + dev_err(&client->dev, "data_len must under 256: len = %d\n", > + data_len); > + return -EIO; > + } > + for (i = 0; i < data_len + 1; i++) > + buf[i] = (i == 0) ? reg : data[i - 1]; > + > + msg.addr = client->addr; > + msg.flags = 0; > + msg.len = data_len + 1; > + msg.buf = buf; > + > + ret = i2c_transfer(client->adapter, &msg, 1); > + if (ret == 1) > + return 0; > + if (ret < 0) > + return ret; > + else > + return -EIO; > +} > + > +static int ps8640_regw_byte(struct i2c_client *client, u8 reg, u8 data) > +{ > + int ret; > + struct i2c_msg msg; > + u8 buf[] = {reg, data}; > + > + msg.addr = client->addr; > + msg.flags = 0; > + msg.len = sizeof(buf); > + msg.buf = buf; > + > + ret = i2c_transfer(client->adapter, &msg, 1); > + if (ret == 1) > + return 0; > + if (ret < 0) > + return ret; > + else > + return -EIO; > +} > + > +static int ps8640_check_valid_id(struct ps8640 *ps_bridge) > +{ > + struct i2c_client *client = ps_bridge->page[4]; > + u8 chip_id[4]; > + > + ps8640_regr(client, PAGE4_REV_L, chip_id, 4); > + > + if ((chip_id[0] == 0x00) && (chip_id[1] == 0x0a) && > + (chip_id[2] == 0x00) && (chip_id[3] == 0x30)) > + return 0; > + > + return -ENODEV; > +} > + > +static void ps8640_show_mcu_fw_version(struct ps8640 *ps_bridge) > +{ > + struct i2c_client *client = ps_bridge->page[5]; > + u8 fw_ver[2]; > + > + ps8640_regr(client, 0x4, fw_ver, 2); > + ps_bridge->info.version = (fw_ver[0] << 8) | fw_ver[1]; > + > + DRM_INFO_ONCE("ps8640 rom fw version %d.%d\n", fw_ver[0], fw_ver[1]); > +} > + > +static int ps8640_bdg_enable(struct ps8640 *ps_bridge) > +{ > + struct i2c_client *client = ps_bridge->page[3]; > + int ret; > + u8 vdo_ctrl[2] = {VDO_CTL_ADD, VDO_EN}; > + > + ret = ps8640_check_valid_id(ps_bridge); > + if (ret) { > + DRM_ERROR("ps8640 not valid: %d\n", ret); > + return ret; > + } > + > + ret = ps8640_regw(client, PAGE3_SET_ADD, vdo_ctrl, 2); > + return ret; > +} > + > +static bool ps8640_in_bootloader(struct ps8640 *ps_bridge) > +{ > + return ps_bridge->in_fw_update; > +} > + > +static void ps8640_prepare(struct ps8640 *ps_bridge) > +{ > + struct i2c_client *client = ps_bridge->page[2]; > + int err, retry_cnt = 0; > + u8 set_vdo_done; > + > + if (ps8640_in_bootloader(ps_bridge)) > + return; > + > + if (ps_bridge->enabled) > + return; > + > + err = drm_panel_prepare(ps_bridge->panel); > + if (err < 0) { > + DRM_ERROR("failed to prepare panel: %d\n", err); > + return; > + } > + > + /* delay for power stable */ > + usleep_range(500, 700); > + > + err = regulator_enable(ps_bridge->pwr_1v2_supply); > + if (err < 0) { > + DRM_ERROR("failed to enable vdd12-supply: %d\n", err); > + goto err_panel_unprepare; > + } > + > + err = regulator_enable(ps_bridge->pwr_3v3_supply); > + if (err < 0) { > + DRM_ERROR("failed to enable vdd33-supply: %d\n", err); > + goto err_regulator_1v2_disable; > + } > + > + gpiod_set_value(ps_bridge->gpio_slp_n, 1); > + gpiod_set_value(ps_bridge->gpio_rst_n, 0); > + usleep_range(500, 700); > + gpiod_set_value(ps_bridge->gpio_rst_n, 1); > + > + /* wait for the ps8640 embed mcu ready > + * first wait 200ms and then check the mcu ready flag every 20ms > + */ > + msleep(200); > + do { > + err = ps8640_regr(client, PAGE2_GPIO_H, &set_vdo_done, 1); > + if (err < 0) { > + DRM_ERROR("failed read PAGE2_GPIO_H: %d\n", err); > + goto err_regulator_1v2_disable; > + } > + msleep(20); > + } while ((retry_cnt++ < 10) && ((set_vdo_done & PS_GPIO9) != PS_GPIO9)); > + > + ps8640_show_mcu_fw_version(ps_bridge); > + ps8640_regw_byte(client, PAGE2_I2C_BYPASS, I2C_BYPASS_EN); > + ps_bridge->enabled = true; > + > + return; > + > +err_regulator_1v2_disable: > + regulator_disable(ps_bridge->pwr_1v2_supply); > +err_panel_unprepare: > + drm_panel_unprepare(ps_bridge->panel); > +} > + > +static void ps8640_pre_enable(struct drm_bridge *bridge) > +{ > + struct ps8640 *ps_bridge = bridge_to_ps8640(bridge); > + > + ps8640_prepare(ps_bridge); > +} > + > +static void ps8640_enable(struct drm_bridge *bridge) > +{ > + struct ps8640 *ps_bridge = bridge_to_ps8640(bridge); > + int err; > + > + err = ps8640_bdg_enable(ps_bridge); > + if (err) > + DRM_ERROR("failed to enable unmutevideo: %d\n", err); > + > + err = drm_panel_enable(ps_bridge->panel); > + if (err < 0) > + DRM_ERROR("failed to enable panel: %d\n", err); > +} > + > +static void ps8640_disable(struct drm_bridge *bridge) > +{ > + struct ps8640 *ps_bridge = bridge_to_ps8640(bridge); > + int err; > + > + if (ps8640_in_bootloader(ps_bridge)) > + return; > + > + if (!ps_bridge->enabled) > + return; > + > + ps_bridge->enabled = false; > + > + err = drm_panel_disable(ps_bridge->panel); > + if (err < 0) > + DRM_ERROR("failed to disable panel: %d\n", err); > + > + gpiod_set_value(ps_bridge->gpio_rst_n, 0); > + gpiod_set_value(ps_bridge->gpio_slp_n, 0); > + regulator_disable(ps_bridge->pwr_3v3_supply); > + regulator_disable(ps_bridge->pwr_1v2_supply); > +} > + > +static void ps8640_post_disable(struct drm_bridge *bridge) > +{ > + struct ps8640 *ps_bridge = bridge_to_ps8640(bridge); > + int err; > + > + err = drm_panel_unprepare(ps_bridge->panel); > + if (err) > + DRM_ERROR("failed to unprepare panel: %d\n", err); > +} > + > +static int ps8640_get_modes(struct drm_connector *connector) > +{ > + struct ps8640 *ps_bridge = connector_to_ps8640(connector); > + > + ps8640_prepare(ps_bridge); > + > + return drm_panel_get_modes(ps_bridge->panel); > +} > + > +static struct drm_encoder *ps8640_best_encoder(struct drm_connector *connector) > +{ > + struct ps8640 *ps_bridge; > + > + ps_bridge = connector_to_ps8640(connector); > + return ps_bridge->bridge.encoder; > +} > + > +static const struct drm_connector_helper_funcs ps8640_connector_helper_funcs = { > + .get_modes = ps8640_get_modes, > + .best_encoder = ps8640_best_encoder, > +}; > + > +static enum drm_connector_status ps8640_detect(struct drm_connector *connector, > + bool force) > +{ > + return connector_status_connected; > +} > + > +static void ps8640_connector_destroy(struct drm_connector *connector) > +{ > + drm_connector_unregister(connector); > + drm_connector_cleanup(connector); > +} > + > +static const struct drm_connector_funcs ps8640_connector_funcs = { > + .dpms = drm_atomic_helper_connector_dpms, > + .fill_modes = drm_helper_probe_single_connector_modes, > + .detect = ps8640_detect, > + .destroy = ps8640_connector_destroy, > + .reset = drm_atomic_helper_connector_reset, > + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, > + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, > +}; > + > +int ps8640_bridge_attach(struct drm_bridge *bridge) > +{ > + struct ps8640 *ps_bridge = bridge_to_ps8640(bridge); > + int ret; > + > + if (!bridge->encoder) { > + DRM_ERROR("Parent encoder object not found"); > + return -ENODEV; > + } > + > + ret = drm_connector_init(bridge->dev, &ps_bridge->connector, > + &ps8640_connector_funcs, > + DRM_MODE_CONNECTOR_eDP); > + > + if (ret) { > + DRM_ERROR("Failed to initialize connector with drm: %d\n", ret); > + return ret; > + } > + > + drm_connector_helper_add(&ps_bridge->connector, > + &ps8640_connector_helper_funcs); > + drm_connector_register(&ps_bridge->connector); > + > + ps_bridge->connector.dpms = DRM_MODE_DPMS_ON; > + drm_mode_connector_attach_encoder(&ps_bridge->connector, > + bridge->encoder); > + > + if (ps_bridge->panel) > + drm_panel_attach(ps_bridge->panel, &ps_bridge->connector); > + > + return ret; > +} > + > +static bool ps8640_bridge_mode_fixup(struct drm_bridge *bridge, > + const struct drm_display_mode *mode, > + struct drm_display_mode *adjusted_mode) > +{ > + return true; > +} > + > +static const struct drm_bridge_funcs ps8640_bridge_funcs = { > + .attach = ps8640_bridge_attach, > + .mode_fixup = ps8640_bridge_mode_fixup, > + .disable = ps8640_disable, > + .post_disable = ps8640_post_disable, > + .pre_enable = ps8640_pre_enable, > + .enable = ps8640_enable, > +}; > + > +static ssize_t ps8640_fw_file_show(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + struct ps8640 *ps_bridge = dev_get_drvdata(dev); > + > + return scnprintf(buf, PAGE_SIZE, "%s\n", ps_bridge->fw_file); > +} > + > +static int ps8640_update_file_name(struct device *dev, char **file_name, > + const char *buf, size_t count) > +{ > + char *new_file_name; > + > + /* Simple sanity check */ > + if (count > 64) { > + dev_warn(dev, "File name too long\n"); > + return -EINVAL; > + } > + > + new_file_name = devm_kmalloc(dev, count + 1, GFP_KERNEL); > + if (!new_file_name) > + return -ENOMEM; > + > + memcpy(new_file_name, buf, count + 1); > + > + /* Echo into the sysfs entry may append newline at the end of buf */ > + if (new_file_name[count - 1] == '\n') > + count--; > + > + new_file_name[count] = '\0'; > + > + if (*file_name) > + devm_kfree(dev, *file_name); > + > + *file_name = new_file_name; > + > + return 0; > +} > + > +static ssize_t ps8640_fw_file_store(struct device *dev, > + struct device_attribute *attr, > + const char *buf, size_t count) > +{ > + struct ps8640 *ps_bridge = dev_get_drvdata(dev); > + int ret; > + > + ret = ps8640_update_file_name(dev, &ps_bridge->fw_file, buf, count); > + if (ret) > + return ret; > + > + return count; > +} > + > +/* Firmware Version is returned as Major.Minor.Build */ > +static ssize_t ps8640_fw_version_show(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + struct ps8640 *ps_bridge = dev_get_drvdata(dev); > + struct ps8640_info *info = &ps_bridge->info; > + > + return scnprintf(buf, PAGE_SIZE, "%u.%u\n", info->version >> 8, > + info->version & 0xff); > +} > + > +/* Hardware Version is returned as FamilyID.VariantID */ > +static ssize_t ps8640_hw_version_show(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + struct ps8640 *ps_bridge = dev_get_drvdata(dev); > + struct ps8640_info *info = &ps_bridge->info; > + > + return scnprintf(buf, PAGE_SIZE, "ps%u.%u\n", info->family_id, > + info->variant_id); > +} > + > +static int ps8640_spi_send_cmd(struct ps8640 *ps_bridge, u8 *cmd, u8 cmd_len) > +{ > + struct i2c_client *client = ps_bridge->page[2]; > + u8 i; > + int ret; > + > + ret = ps8640_regw_byte(client, PAGE2_IROM_CTRL, IROM_ENABLE); > + if (ret) > + goto err; > + > + /* write command in write port */ > + for (i = 0; i < cmd_len; i++) { > + ret = ps8640_regw_byte(client, PAGE2_SWSPI_WDATA, cmd[i]); > + if (ret) > + goto err; > + } > + /* command length */ > + ret = ps8640_regw_byte(client, PAGE2_SWSPI_LEN, cmd_len - 1); > + if (ret) > + goto err; > + /* trigger read */ > + ret = ps8640_regw_byte(client, PAGE2_SWSPI_CTL, TRIGGER_NO_READBACK); > + if (ret) > + goto err; > + > + ret = ps8640_regw_byte(client, PAGE2_IROM_CTRL, IROM_DISABLE); > + if (ret) > + goto err; > + > + return 0; > + > +err: > + dev_err(&client->dev, "send command err: %d\n", ret); > + return ret; > +} > + > +static int ps8640_wait_spi_ready(struct ps8640 *ps_bridge) > +{ > + struct i2c_client *client = ps_bridge->page[2]; > + u8 spi_rdy_st, retry_cnt = 0; > + > + do { > + ps8640_regr(client, PAGE2_SPI_STATUS, &spi_rdy_st, 1); > + msleep(20); > + if ((retry_cnt == SPI_MAX_RETRY_CNT) && > + ((spi_rdy_st & 0x0c) != 0x0c)) { > + dev_err(&client->dev, "wait spi ready timeout\n"); > + return -EBUSY; > + } > + } while ((retry_cnt++ < SPI_MAX_RETRY_CNT) && > + (spi_rdy_st & 0x0c) == 0x0c); > + > + return 0; > +} > + > +static int ps8640_wait_spi_nobusy(struct ps8640 *ps_bridge) > +{ > + struct i2c_client *client = ps_bridge->page[2]; > + u8 spi_status, retry_cnt = 0; > + int ret; > + > + do { > + /* 0x05 RDSR; Read-Status-Register */ > + ret = ps8640_regw_byte(client, PAGE2_SWSPI_WDATA, 0x05); > + if (ret) > + goto err_send_cmd_exit; > + > + /* command length */ > + ret = ps8640_regw_byte(client, PAGE2_SWSPI_LEN, 0x00); > + if (ret) > + goto err_send_cmd_exit; > + > + /* trigger read */ > + ret = ps8640_regw_byte(client, PAGE2_SWSPI_CTL, > + TRIGGER_READBACK); > + if (ret) > + goto err_send_cmd_exit; > + > + /* delay for cmd send */ > + usleep_range(100, 300); > + /* wait for SPI ROM until not busy */ > + ret = ps8640_regr(client, PAGE2_SWSPI_RDATA, &spi_status, 1); > + if (ret) > + goto err_send_cmd_exit; > + } while ((retry_cnt++ < SPI_MAX_RETRY_CNT) && > + (spi_status & 0x0c) == 0x0c); > + > + if ((retry_cnt > SPI_MAX_RETRY_CNT) && (spi_status & 0x0c) != 0x0c) { > + ret = -EBUSY; > + dev_err(&client->dev, "wait spi no busy timeout: %d\n", ret); > + goto err_timeout; > + } > + > + return 0; > + > +err_send_cmd_exit: > + dev_err(&client->dev, "send command err: %d\n", ret); > + > +err_timeout: > + return ret; > +} > + > +static int ps8640_wait_rom_idle(struct ps8640 *ps_bridge) > +{ > + struct i2c_client *client = ps_bridge->page[0]; > + int ret; > + > + ret = ps8640_regw_byte(client, PAGE2_IROM_CTRL, IROM_ENABLE); > + if (ret) > + goto exit; > + > + ret = ps8640_wait_spi_ready(ps_bridge); > + if (ret) > + goto exit; > + > + ret = ps8640_wait_spi_nobusy(ps_bridge); > + if (ret) > + goto exit; > + > + ret = ps8640_regw_byte(client, PAGE2_IROM_CTRL, IROM_DISABLE); > + if (ret) > + goto exit; > + > +exit: > + if (ret) > + dev_err(&client->dev, "wait ps8640 rom idle fail: %d\n", ret); > + > + return ret; > +} > + > +static int ps8640_spi_dl_mode(struct ps8640 *ps_bridge) > +{ > + struct i2c_client *client = ps_bridge->page[2]; > + int ret; > + > + /* switch ps8640 mode to spi dl mode */ > + gpiod_set_value(ps_bridge->gpio_mode_sel_n, 0); > + > + /* reset spi interface */ > + ret = ps8640_regw_byte(client, PAGE2_SW_REST, 0xc0); > + if (ret) > + goto exit; > + > + ret = ps8640_regw_byte(client, PAGE2_SW_REST, 0x40); > + if (ret) > + goto exit; > + > +exit: > + if (ret) > + dev_err(&client->dev, "fail reset spi interface: %d\n", ret); > + > + return ret; > +} > + > +static int ps8640_rom_prepare(struct ps8640 *ps_bridge) > +{ > + struct i2c_client *client = ps_bridge->page[2]; > + struct device *dev = &client->dev; > + u8 i, cmd[2]; > + int ret; > + > + /* Enable-Write-Status-Register */ > + cmd[0] = 0x06; > + ret = ps8640_spi_send_cmd(ps_bridge, cmd, 1); > + if (ret) { > + dev_err(dev, "failed enable-write-status-register: %d\n", ret); > + return ret; > + } > + > + /* disable all protection */ > + cmd[0] = 0x01; > + cmd[1] = 0x00; > + ret = ps8640_spi_send_cmd(ps_bridge, cmd, 2); > + if (ret) { > + dev_err(dev, "fail disable all protection: %d\n", ret); > + return ret; > + } > + > + /* wait for SPI module ready */ > + ret = ps8640_wait_rom_idle(ps_bridge); > + if (ret) { > + dev_err(dev, "fail wait rom idle: %d\n", ret); > + return ret; > + } > + > + ps8640_regw_byte(client, PAGE2_IROM_CTRL, IROM_ENABLE); > + for (i = 0; i < 6; i++) > + ps8640_regw_byte(client, PAGE2_ENCTLSPI_WR, enc_ctrl_code[i]); > + ps8640_regw_byte(client, PAGE2_IROM_CTRL, IROM_DISABLE); > + > + /* Enable-Write-Status-Register */ > + cmd[0] = 0x06; > + ret = ps8640_spi_send_cmd(ps_bridge, cmd, 1); > + if (ret) { > + dev_err(dev, "fail enable-write-status-register: %d\n", ret); > + return ret; > + } > + > + /* chip erase command */ > + cmd[0] = 0xc7; > + ret = ps8640_spi_send_cmd(ps_bridge, cmd, 1); > + if (ret) { > + dev_err(dev, "fail disable all protection: %d\n", ret); > + return ret; > + } > + > + ret = ps8640_wait_rom_idle(ps_bridge); > + if (ret) { > + dev_err(dev, "fail wait rom idle: %d\n", ret); > + return ret; > + } > + > + return 0; > +} > + > +static int ps8640_validate_firmware(struct ps8640 *ps_bridge, > + const struct firmware *fw) > +{ > + struct i2c_client *client = ps_bridge->page[0]; > + struct ps8640_info *info = &ps_bridge->info; > + u16 fw_chip_id, fw_version_id; > + > + /* > + * Get the chip_id from the firmware. Make sure that it is the > + * right controller to do the firmware and config update. > + */ > + fw_chip_id = get_unaligned_le16(fw->data + FW_CHIP_ID_OFFSET); > + > + if (fw_chip_id != 0x8640) { > + dev_err(&client->dev, > + "chip id mismatch: fw 0x%x vs. chip 0x8640\n", > + fw_chip_id); > + return -ENODEV; > + } > + > + fw_version_id = get_unaligned_le16(fw->data + FW_VERSION_OFFSET); > + > + if (fw_chip_id != info->version) { > + dev_err(&client->dev, > + "fw version mismatch: fw %d.%d vs. chip %d.%d\n", > + fw_version_id >> 8, fw_version_id & 0xff, > + info->version >> 8, info->version & 0xff); > + return -ENODEV; > + } > + > + return 0; > +} > + > +static int ps8640_write_rom(struct ps8640 *ps_bridge, const struct firmware *fw) > +{ > + struct i2c_client *client = ps_bridge->page[0]; > + struct device *dev = &client->dev; > + struct i2c_client *client2 = ps_bridge->page[2]; > + struct i2c_client *client7 = ps_bridge->page[7]; > + unsigned int pos = 0; > + u8 progress_cnt, rom_page_id[2]; > + int ret; > + > + ps8640_regw_byte(client2, PAGE2_SPI_CFG3, I2C_TO_SPI_RESET); > + msleep(100); > + ps8640_regw_byte(client2, PAGE2_SPI_CFG3, 0x00); > + > + while (pos < fw->size) { > + rom_page_id[0] = (pos >> 8) & 0xFF; > + rom_page_id[1] = (pos >> 16) & 0xFF; > + ret = ps8640_regw(client2, PAGE2_ROMADD_BYTE1, rom_page_id, 2); > + if (ret) > + goto error; > + ret = ps8640_regw(client7, 0, fw->data + pos, 256); > + if (ret) > + goto error; > + > + pos += 256; > + if (progress_cnt != (pos * 100) / fw->size) { > + progress_cnt = (pos * 100) / fw->size; > + dev_info(dev, "fw update progress percent %d\n", > + progress_cnt); > + } > + } > + return 0; > + > +error: > + dev_err(dev, "failed write extenal flash, %d\n", ret); > + return ret; > +} > + > +static int ps8640_spi_normal_mode(struct ps8640 *ps_bridge) > +{ > + u8 cmd[2]; > + struct i2c_client *client = ps_bridge->page[2]; > + > + /* Enable-Write-Status-Register */ > + cmd[0] = 0x06; > + ps8640_spi_send_cmd(ps_bridge, cmd, 1); > + > + /* protect BPL/BP0/BP1 */ > + cmd[0] = 0x01; > + cmd[1] = 0x8c; > + ps8640_spi_send_cmd(ps_bridge, cmd, 2); > + > + /* wait for SPI rom ready */ > + ps8640_wait_rom_idle(ps_bridge); > + > + /* disable PS8640 mapping function */ > + ps8640_regw_byte(client, PAGE2_ENCTLSPI_WR, 0x00); > + > + gpiod_set_value(ps_bridge->gpio_mode_sel_n, 1); > + return 0; > +} > + > +static int ps8640_enter_bl(struct ps8640 *ps_bridge) > +{ > + int ret; > + > + ret = ps8640_spi_dl_mode(ps_bridge); > + if (ret) > + return ret; > + > + ps_bridge->in_fw_update = true; > + return 0; > +} > + > +static void ps8640_exit_bl(struct ps8640 *ps_bridge, const struct firmware *fw) > +{ > + ps_bridge->in_fw_update = false; > + ps8640_spi_normal_mode(ps_bridge); > +} > + > +static int ps8640_load_fw(struct ps8640 *ps_bridge, const struct firmware *fw) > +{ > + struct i2c_client *client = ps_bridge->page[0]; > + struct device *dev = &client->dev; > + int ret; > + bool ps8640_status_backup = ps_bridge->enabled; > + > + if (!ps8640_in_bootloader(ps_bridge)) { > + if (!ps8640_status_backup) > + ps8640_prepare(ps_bridge); > + > + ret = ps8640_enter_bl(ps_bridge); > + if (ret) > + goto exit; > + } > + > + ret = ps8640_validate_firmware(ps_bridge, fw); > + if (ret) > + goto exit; > + > + ret = ps8640_rom_prepare(ps_bridge); > + if (ret) > + goto exit; > + > + ret = ps8640_write_rom(ps_bridge, fw); > + > +exit: > + if (ret) > + dev_err(dev, "Failed to load firmware, %d\n", ret); > + > + ps8640_exit_bl(ps_bridge, fw); > + if (!ps8640_status_backup) > + ps8640_disable(&ps_bridge->bridge); > + return ret; > +} > + > +static ssize_t ps8640_update_fw_store(struct device *dev, > + struct device_attribute *attr, > + const char *buf, size_t count) > +{ > + struct i2c_client *client = to_i2c_client(dev); > + struct ps8640 *ps_bridge = i2c_get_clientdata(client); > + const struct firmware *fw; > + int error; > + > + error = request_firmware(&fw, ps_bridge->fw_file, dev); > + if (error) { > + dev_err(dev, "Unable to open firmware %s: %d\n", > + ps_bridge->fw_file, error); > + return error; > + } > + > + error = ps8640_load_fw(ps_bridge, fw); > + if (error) > + dev_err(dev, "The firmware update failed(%d)\n", error); > + else > + dev_info(dev, "The firmware update succeeded\n"); > + > + release_firmware(fw); > + return error ? error : count; > +} > + > +static DEVICE_ATTR(fw_file, S_IRUGO | S_IWUSR, ps8640_fw_file_show, > + ps8640_fw_file_store); > +static DEVICE_ATTR(fw_version, S_IRUGO, ps8640_fw_version_show, NULL); > +static DEVICE_ATTR(hw_version, S_IRUGO, ps8640_hw_version_show, NULL); > +static DEVICE_ATTR(update_fw, S_IWUSR, NULL, ps8640_update_fw_store); > + > +static struct attribute *ps8640_attrs[] = { > + &dev_attr_fw_file.attr, > + &dev_attr_fw_version.attr, > + &dev_attr_hw_version.attr, > + &dev_attr_update_fw.attr, > + NULL > +}; > + > +static const struct attribute_group ps8640_attr_group = { > + .attrs = ps8640_attrs, > +}; > + > +static void ps8640_remove_sysfs_group(void *data) > +{ > + struct ps8640 *ps_bridge = data; > + > + sysfs_remove_group(&ps_bridge->page[0]->dev.kobj, &ps8640_attr_group); > +} > + > +/* for fireware update end */ > + > +static int ps8640_probe(struct i2c_client *client, > + const struct i2c_device_id *id) > +{ > + struct device *dev = &client->dev; > + struct ps8640 *ps_bridge; > + struct device_node *np = dev->of_node; > + struct device_node *port, *out_ep; > + struct device_node *panel_node = NULL; > + int i, ret; > + > + ps_bridge = devm_kzalloc(dev, sizeof(*ps_bridge), GFP_KERNEL); > + if (!ps_bridge) { > + ret = -ENOMEM; > + goto exit; > + } > + /* port@1 is ps8640 output port */ > + port = of_graph_get_port_by_id(np, 1); > + if (port) { > + out_ep = of_get_child_by_name(port, "endpoint"); > + of_node_put(port); > + if (out_ep) { > + panel_node = of_graph_get_remote_port_parent(out_ep); > + of_node_put(out_ep); > + } > + } > + if (panel_node) { > + ps_bridge->panel = of_drm_find_panel(panel_node); > + of_node_put(panel_node); > + if (!ps_bridge->panel) { > + ret = -EPROBE_DEFER; > + goto exit; > + } > + } > + > + ps_bridge->pwr_3v3_supply = devm_regulator_get(dev, "vdd33"); > + if (IS_ERR(ps_bridge->pwr_3v3_supply)) { > + ret = PTR_ERR(ps_bridge->pwr_3v3_supply); > + dev_err(dev, "cannot get vdd33 supply: %d\n", ret); > + goto exit; > + } > + > + ps_bridge->pwr_1v2_supply = devm_regulator_get(dev, "vdd12"); > + if (IS_ERR(ps_bridge->pwr_1v2_supply)) { > + ret = PTR_ERR(ps_bridge->pwr_1v2_supply); > + dev_err(dev, "cannot get vdd12 supply: %d\n", ret); > + goto exit; > + } > + > + ps_bridge->gpio_mode_sel_n = devm_gpiod_get(&client->dev, "mode-sel", > + GPIOD_OUT_HIGH); > + if (IS_ERR(ps_bridge->gpio_mode_sel_n)) { > + ret = PTR_ERR(ps_bridge->gpio_mode_sel_n); > + dev_err(dev, "cannot get gpio_mode_sel_n %d\n", ret); > + goto exit; > + } > + > + ps_bridge->gpio_slp_n = devm_gpiod_get(&client->dev, "sleep", > + GPIOD_OUT_HIGH); > + if (IS_ERR(ps_bridge->gpio_slp_n)) { > + ret = PTR_ERR(ps_bridge->gpio_slp_n); > + dev_err(dev, "cannot get gpio_slp_n: %d\n", ret); > + goto exit; > + } > + > + ps_bridge->gpio_rst_n = devm_gpiod_get(&client->dev, "reset", > + GPIOD_OUT_HIGH); > + if (IS_ERR(ps_bridge->gpio_rst_n)) { > + ret = PTR_ERR(ps_bridge->gpio_rst_n); > + dev_err(dev, "cannot get gpio_rst_n: %d\n", ret); > + goto exit; > + } > + > + ps_bridge->bridge.funcs = &ps8640_bridge_funcs; > + ps_bridge->bridge.of_node = dev->of_node; > + ret = drm_bridge_add(&ps_bridge->bridge); > + if (ret) { > + dev_err(dev, "Failed to add bridge: %d\n", ret); > + goto exit; > + } > + > + ret = ps8640_update_file_name(&client->dev, &ps_bridge->fw_file, > + PS_FW_NAME, strlen(PS_FW_NAME)); > + if (ret) { > + dev_err(dev, "failed to update file name: %d\n", ret); > + goto exit; > + } > + > + ps_bridge->page[0] = client; > + > + /* ps8640 uses multiple addresses, use dummy devices for them > + * page[0]: for DP control > + * page[1]: for VIDEO Bridge > + * page[2]: for control top > + * page[3]: for DSI Link Control1 > + * page[4]: for MIPI Phy > + * page[5]: for VPLL > + * page[6]: for DSI Link Control2 > + * page[7]: for spi rom mapping > + */ > + for (i = 1; i < 8; i++) { > + ps_bridge->page[i] = i2c_new_dummy(client->adapter, > + client->addr + i); > + if (!ps_bridge->page[i]) { > + dev_err(dev, "failed i2c dummy device, address%02x\n", > + client->addr + i); > + ret = -EBUSY; > + goto exit_dummy; > + } > + } > + i2c_set_clientdata(client, ps_bridge); > + > + ret = sysfs_create_group(&client->dev.kobj, &ps8640_attr_group); > + if (ret) { > + dev_err(dev, "failed to create sysfs entries: %d\n", ret); > + goto exit_dummy; > + } > + > + ret = devm_add_action(dev, ps8640_remove_sysfs_group, ps_bridge); > + if (ret) { > + ps8640_remove_sysfs_group(ps_bridge); > + dev_err(dev, "failed to add sysfs cleanup action: %d\n", ret); > + goto exit_dummy; > + } > + return 0; > + > +exit_dummy: > + for (i = 1; i < 8; i++) > + if (ps_bridge->page[i]) > + i2c_unregister_device(ps_bridge->page[i]); > + > +exit: > + return ret; > +} > + > +static int ps8640_remove(struct i2c_client *client) > +{ > + struct ps8640 *ps_bridge = i2c_get_clientdata(client); > + int i; > + > + for (i = 1; i < 8; i++) > + if (ps_bridge->page[i]) > + i2c_unregister_device(ps_bridge->page[i]); > + > + drm_bridge_remove(&ps_bridge->bridge); > + > + return 0; > +} > + > +static const struct i2c_device_id ps8640_i2c_table[] = { > + {"parade,ps8640", 0}, > + {}, > +}; > +MODULE_DEVICE_TABLE(i2c, ps8640_i2c_table); > + > +static const struct of_device_id ps8640_match[] = { > + { .compatible = "parade,ps8640" }, > + {}, > +}; > +MODULE_DEVICE_TABLE(of, ps8640_match); > + > +static struct i2c_driver ps8640_driver = { > + .id_table = ps8640_i2c_table, > + .probe = ps8640_probe, > + .remove = ps8640_remove, > + .driver = { > + .name = "parade,ps8640", > + .of_match_table = ps8640_match, > + }, > +}; > +module_i2c_driver(ps8640_driver); > + > +MODULE_AUTHOR("Jitao Shi "); > +MODULE_AUTHOR("CK Hu "); > +MODULE_DESCRIPTION("PARADE ps8640 DSI-eDP converter driver"); > +MODULE_LICENSE("GPL v2"); > -- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, hosted by The Linux Foundation